def test_set_regs():
    bus = MockSMBus(1)
    device = Device(0x00,
                    i2c_dev=bus,
                    registers=(Register('test',
                                        0x00,
                                        fields=(BitField('test', 0xFF), )), ))
    device.set('test', test=123)

    assert device.get('test').test == 123

    assert bus.regs[0] == 123
def test_get_regs():
    bus = MockSMBus(1)
    device = Device(0x00,
                    i2c_dev=bus,
                    registers=(Register('test',
                                        0x00,
                                        fields=(
                                            BitField('test', 0xFF00),
                                            BitField('monkey', 0x00FF),
                                        ),
                                        bit_width=16), ))
    device.set('test', test=0x66, monkey=0x77)

    reg = device.get('test')
    reg.test == 0x66
    reg.monkey == 0x77

    assert bus.regs[0] == 0x66
    assert bus.regs[1] == 0x77
Beispiel #3
0
class PCA9554A:
    def __init__(self, i2c_addr=0x38, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._is_setup = False
        # Device definition
        self._pca9554a = Device(
            self._i2c_addr,
            i2c_dev=self._i2c_dev,
            bit_width=8,
            registers=(
                Register('INPUT',
                         0x00,
                         fields=(BitField('value', 0xFF),
                                 BitField('switch', 0b00001000),
                                 BitField('led', 0b00000001))),
                Register('OUTPUT',
                         0x01,
                         fields=(BitField('value', 0xFF),
                                 BitField('switch', 0b00001000),
                                 BitField('led', 0b00000001))),
                Register('INVERT',
                         0x02,
                         fields=(BitField('value', 0xFF),
                                 BitField('switch', 0b00001000),
                                 BitField('led', 0b00000001))),
                Register('CONFIG',
                         0x03,
                         fields=(BitField('value', 0xFF),
                                 BitField('switch', 0b00001000),
                                 BitField('led', 0b00000001))),
            ))
        #  Set IO configuration for driving switch and LED
        self._pca9554a.set('OUTPUT', switch=0, led=1)
        self._pca9554a.set('CONFIG', switch=0, led=0)
        self.led_enable = True
        self.switch_enabled = True
        self.led_status = False
        self.switch_status = False
Beispiel #4
0
class BMP280:
    def __init__(self, i2c_addr=I2C_ADDRESS_GND, i2c_dev=None):
        self.calibration = BMP280Calibration()
        self._is_setup = False
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._bmp280 = Device([I2C_ADDRESS_GND, I2C_ADDRESS_VCC], i2c_dev=self._i2c_dev, bit_width=8, registers=(
            Register('CHIP_ID', 0xD0, fields=(
                BitField('id', 0xFF),
            )),
            Register('RESET', 0xE0, fields=(
                BitField('reset', 0xFF),
            )),
            Register('STATUS', 0xF3, fields=(
                BitField('measuring', 0b00001000),  # 1 when conversion is running
                BitField('im_update', 0b00000001),  # 1 when NVM data is being copied
            )),
            Register('CTRL_MEAS', 0xF4, fields=(
                BitField('osrs_t', 0b11100000,   # Temperature oversampling
                         adapter=LookupAdapter({
                             1: 0b001,
                             2: 0b010,
                             4: 0b011,
                             8: 0b100,
                             16: 0b101
                         })),
                BitField('osrs_p', 0b00011100,   # Pressure oversampling
                         adapter=LookupAdapter({
                             1: 0b001,
                             2: 0b010,
                             4: 0b011,
                             8: 0b100,
                             16: 0b101})),
                BitField('mode', 0b00000011,     # Power mode
                         adapter=LookupAdapter({
                             'sleep': 0b00,
                             'forced': 0b10,
                             'normal': 0b11})),
            )),
            Register('CONFIG', 0xF5, fields=(
                BitField('t_sb', 0b11100000,     # Temp standby duration in normal mode
                         adapter=LookupAdapter({
                             0.5: 0b000,
                             62.5: 0b001,
                             125: 0b010,
                             250: 0b011,
                             500: 0b100,
                             1000: 0b101,
                             2000: 0b110,
                             4000: 0b111})),
                BitField('filter', 0b00011100),                   # Controls the time constant of the IIR filter
                BitField('spi3w_en', 0b0000001, read_only=True),  # Enable 3-wire SPI interface when set to 1. IE: Don't set this bit!
            )),
            Register('DATA', 0xF7, fields=(
                BitField('temperature', 0x000000FFFFF0),
                BitField('pressure', 0xFFFFF0000000),
            ), bit_width=48),
            Register('CALIBRATION', 0x88, fields=(
                BitField('dig_t1', 0xFFFF << 16 * 11, adapter=U16Adapter()),   # 0x88 0x89
                BitField('dig_t2', 0xFFFF << 16 * 10, adapter=S16Adapter()),   # 0x8A 0x8B
                BitField('dig_t3', 0xFFFF << 16 * 9, adapter=S16Adapter()),    # 0x8C 0x8D
                BitField('dig_p1', 0xFFFF << 16 * 8, adapter=U16Adapter()),    # 0x8E 0x8F
                BitField('dig_p2', 0xFFFF << 16 * 7, adapter=S16Adapter()),    # 0x90 0x91
                BitField('dig_p3', 0xFFFF << 16 * 6, adapter=S16Adapter()),    # 0x92 0x93
                BitField('dig_p4', 0xFFFF << 16 * 5, adapter=S16Adapter()),    # 0x94 0x95
                BitField('dig_p5', 0xFFFF << 16 * 4, adapter=S16Adapter()),    # 0x96 0x97
                BitField('dig_p6', 0xFFFF << 16 * 3, adapter=S16Adapter()),    # 0x98 0x99
                BitField('dig_p7', 0xFFFF << 16 * 2, adapter=S16Adapter()),    # 0x9A 0x9B
                BitField('dig_p8', 0xFFFF << 16 * 1, adapter=S16Adapter()),    # 0x9C 0x9D
                BitField('dig_p9', 0xFFFF << 16 * 0, adapter=S16Adapter()),    # 0x9E 0x9F
            ), bit_width=192)
        ))

    def setup(self, mode='normal', temperature_oversampling=16, pressure_oversampling=16, temperature_standby=500):
        if self._is_setup:
            return
        self._is_setup = True

        self._bmp280.select_address(self._i2c_addr)
        self._mode = mode

        if mode == "forced":
            mode = "sleep"

        try:
            chip = self._bmp280.get('CHIP_ID')
            if chip.id != CHIP_ID:
                raise RuntimeError("Unable to find bmp280 on 0x{:02x}, CHIP_ID returned {:02x}".format(self._i2c_addr, chip.id))
        except IOError:
            raise RuntimeError("Unable to find bmp280 on 0x{:02x}, IOError".format(self._i2c_addr))

        self._bmp280.set('CTRL_MEAS',
                         mode=mode,
                         osrs_t=temperature_oversampling,
                         osrs_p=pressure_oversampling)

        self._bmp280.set('CONFIG',
                         t_sb=temperature_standby,
                         filter=2)

        self.calibration.set_from_namedtuple(self._bmp280.get('CALIBRATION'))

    def update_sensor(self):
        self.setup()

        if self._mode == "forced":
            # Trigger a reading in forced mode and wait for result
            self._bmp280.set("CTRL_MEAS", mode="forced")
            while self._bmp280.get("STATUS").measuring:
                time.sleep(0.001)

        raw = self._bmp280.get('DATA')

        self.temperature = self.calibration.compensate_temperature(raw.temperature)
        self.pressure = self.calibration.compensate_pressure(raw.pressure) / 100.0

    def get_temperature(self):
        self.update_sensor()
        return self.temperature

    def get_pressure(self):
        self.update_sensor()
        return self.pressure

    def get_altitude(self, qnh=1013.25):
        self.update_sensor()
        pressure = self.get_pressure()
        temperature = self.get_temperature()
        altitude = ((pow((qnh / pressure), (1.0 / 5.257)) - 1) * (temperature + 273.15)) / 0.0065
        return altitude
Beispiel #5
0
class MCP9600:
    def __init__(self, i2c_addr=I2C_ADDRESS_DEFAULT, i2c_dev=None):
        self._is_setup = False
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._mcp9600 = Device(I2C_ADDRESSES, i2c_dev=self._i2c_dev, bit_width=8, registers=(
            Register('HOT_JUNCTION', 0x00, fields=(
                BitField('temperature', 0xFFFF, adapter=TemperatureAdapter()),
            ), bit_width=16),
            Register('DELTA', 0x01, fields=(
                BitField('value', 0xFFFF, adapter=TemperatureAdapter()),
            ), bit_width=16),
            Register('COLD_JUNCTION', 0x02, fields=(
                BitField('temperature', 0x1FFF, adapter=TemperatureAdapter()),
            ), bit_width=16),
            Register('RAW_DATA', 0x03, fields=(
                BitField('adc', 0xFFFFFF),
            ), bit_width=24),
            Register('STATUS', 0x04, fields=(
                BitField('burst_complete', 0b10000000),
                BitField('updated', 0b01000000),
                BitField('input_range', 0b00010000),
                BitField('alert_4', 0b00001000),
                BitField('alert_3', 0b00000100),
                BitField('alert_2', 0b00000010),
                BitField('alert_1', 0b00000001)
            )),
            Register('THERMOCOUPLE_CONFIG', 0x05, fields=(
                BitField('type_select', 0b01110000, adapter=LookupAdapter({
                    'K': 0b000,
                    'J': 0b001,
                    'T': 0b010,
                    'N': 0b011,
                    'S': 0b100,
                    'E': 0b101,
                    'B': 0b110,
                    'R': 0b111
                })),
                BitField('filter_coefficients', 0b00000111)
            )),
            Register('DEVICE_CONFIG', 0x06, fields=(
                BitField('cold_junction_resolution', 0b10000000, adapter=LookupAdapter({
                    0.0625: 0b0,
                    0.25: 0b1
                })),
                BitField('adc_resolution', 0b01100000, adapter=LookupAdapter({
                    18: 0b00,
                    16: 0b01,
                    14: 0b10,
                    12: 0b11
                })),
                BitField('burst_mode_samples', 0b00011100, adapter=LookupAdapter({
                    1: 0b000,
                    2: 0b001,
                    4: 0b010,
                    8: 0b011,
                    16: 0b100,
                    32: 0b101,
                    64: 0b110,
                    128: 0b111
                })),
                BitField('shutdown_modes', 0b00000011, adapter=LookupAdapter({
                    'Normal': 0b00,
                    'Shutdown': 0b01,
                    'Burst': 0b10
                }))
            )),
            Register('ALERT1_CONFIG', 0x08, fields=(
                BitField('clear_interrupt', 0b10000000),
                BitField('monitor_junction', 0b00010000),  # 1 Cold Junction, 0 Thermocouple
                BitField('rise_fall', 0b00001000),         # 1 rising, 0 cooling
                BitField('state', 0b00000100),             # 1 active high, 0 active low
                BitField('mode', 0b00000010),              # 1 interrupt mode, 0 comparator mode
                BitField('enable', 0b00000001)             # 1 enable, 0 disable
            )),
            Register('ALERT2_CONFIG', 0x09, fields=(
                BitField('clear_interrupt', 0b10000000),
                BitField('monitor_junction', 0b00010000),  # 1 Cold Junction, 0 Thermocouple
                BitField('rise_fall', 0b00001000),         # 1 rising, 0 cooling
                BitField('state', 0b00000100),             # 1 active high, 0 active low
                BitField('mode', 0b00000010),              # 1 interrupt mode, 0 comparator mode
                BitField('enable', 0b00000001)             # 1 enable, 0 disable
            )),
            Register('ALERT3_CONFIG', 0x0A, fields=(
                BitField('clear_interrupt', 0b10000000),
                BitField('monitor_junction', 0b00010000),  # 1 Cold Junction, 0 Thermocouple
                BitField('rise_fall', 0b00001000),         # 1 rising, 0 cooling
                BitField('state', 0b00000100),             # 1 active high, 0 active low
                BitField('mode', 0b00000010),              # 1 interrupt mode, 0 comparator mode
                BitField('enable', 0b00000001)             # 1 enable, 0 disable
            )),
            Register('ALERT4_CONFIG', 0x0B, fields=(
                BitField('clear_interrupt', 0b10000000),
                BitField('monitor_junction', 0b00010000),  # 1 Cold Junction, 0 Thermocouple
                BitField('rise_fall', 0b00001000),         # 1 rising, 0 cooling
                BitField('state', 0b00000100),             # 1 active high, 0 active low
                BitField('mode', 0b00000010),              # 1 interrupt mode, 0 comparator mode
                BitField('enable', 0b00000001)             # 1 enable, 0 disable
            )),
            Register('ALERT1_HYSTERESIS', 0x0C, fields=(
                BitField('value', 0xFF),
            )),
            Register('ALERT2_HYSTERESIS', 0x0D, fields=(
                BitField('value', 0xFF),
            )),
            Register('ALERT3_HYSTERESIS', 0x0E, fields=(
                BitField('value', 0xFF),
            )),
            Register('ALERT4_HYSTERESIS', 0x0F, fields=(
                BitField('value', 0xFF),
            )),
            Register('ALERT1_LIMIT', 0x10, fields=(
                BitField('value', 0xFFFF, adapter=AlertLimitAdapter()),
            ), bit_width=16),
            Register('ALERT2_LIMIT', 0x11, fields=(
                BitField('value', 0xFFFF, adapter=AlertLimitAdapter()),
            ), bit_width=16),
            Register('ALERT3_LIMIT', 0x12, fields=(
                BitField('value', 0xFFFF, adapter=AlertLimitAdapter()),
            ), bit_width=16),
            Register('ALERT4_LIMIT', 0x13, fields=(
                BitField('value', 0xFFFF, adapter=AlertLimitAdapter()),
            ), bit_width=16),
            Register('CHIP_ID', 0x20, fields=(
                BitField('id', 0xFF00),
                BitField('revision', 0x00FF, adapter=RevisionAdapter())
            ), bit_width=16)
        ))

        self.alert_registers = [
            'ALERT1_CONFIG',
            'ALERT2_CONFIG',
            'ALERT3_CONFIG',
            'ALERT4_CONFIG'
        ]
        self.alert_limits = [
            'ALERT1_LIMIT',
            'ALERT2_LIMIT',
            'ALERT3_LIMIT',
            'ALERT4_LIMIT'
        ]
        self.alert_hysteresis = [
            'ALERT1_HYSTERESIS',
            'ALERT2_HYSTERESIS',
            'ALERT3_HYSTERESIS',
            'ALERT4_HYSTERESIS'
        ]

        self._mcp9600.select_address(self._i2c_addr)

        try:
            chip = self._mcp9600.get('CHIP_ID')
            if chip.id != CHIP_ID:
                raise RuntimeError("Unable to find mcp9600 on 0x{:02x}, CHIP_ID returned {:02x}".format(self._i2c_addr, chip.id))
        except IOError:
            raise RuntimeError("Unable to find mcp9600 on 0x{:02x}, IOError".format(self._i2c_addr))

    def setup(self):
        pass

    def set_thermocouple_type(self, thermocouple_type):
        """Set the type of thermocouple connected to the MCP9600.

        :param thermocouple_type: One of 'K', 'J', 'T', 'N', 'S', 'E', 'B' or 'R'

        """
        self._mcp9600.set('THERMOCOUPLE_CONFIG', type_select=thermocouple_type)

    def get_thermocouple_type(self):
        """Get the type of thermocouple connected to the MCP9600.

        Returns one of 'K', 'J', 'T', 'N', 'S', 'E', 'B' or 'R'

        """
        return self._mcp9600.get('THERMOCOUPLE_CONFIG').type_select

    def get_hot_junction_temperature(self):
        """Return the temperature measured by the attached thermocouple."""
        return self._mcp9600.get('HOT_JUNCTION').temperature

    def get_cold_junction_temperature(self):
        """Return the temperature measured by the onboard sensor."""
        return self._mcp9600.get('COLD_JUNCTION').temperature

    def get_temperature_delta(self):
        """Return the difference between hot and cold junction temperatures."""
        return self._mcp9600.get('DELTA').value

    def check_alerts(self):
        """Check status flags of all alert registers."""
        status = self._mcp9600.get('STATUS')
        return status.alert_1, status.alert_2, status.alert_3, status.alert_4

    def clear_alert(self, index):
        """Clear the interrupt flag on an alert slot.

        :param index: Index of alert to clear, from 1 to 4

        """
        self._mcp9600.set(self.alert_registers[index - 1], clear_interrupt=1)

    def get_alert_hysteresis(self, index):
        alert_hysteresis = self.alert_hysteresis[index - 1]
        return self._mcp9600.get(alert_hysteresis).value

    def get_alert_limit(self, index):
        alert_limit = self.alert_limits[index - 1]
        return self._mcp9600.get(alert_limit).value

    def configure_alert(self, index, limit=None, hysteresis=None, clear_interrupt=True, monitor_junction=0, rise_fall=1, state=1, mode=1, enable=False):
        """Set up one of the 4 alert slots.

        :param index: Index of alert to set, from 1 to 4
        :param limit: Temperature limit
        :param hysteresis: Temperature hysteresis
        :param clear_interrupt: Whether to clear the interrupt flag
        :param monitor_junction: Which junction to monitor: 0 = HOT, 1 = COLD
        :param rise_fall: Monitor for 1=rising or 0=falling temperature
        :param state: Active 1=high or 0=low
        :param mode: 1=Interrupt mode, must clear interrupt to de-assert, 0=Comparator mode
        :param enable: True=Enabled, False=Disabled

        """
        alert_register = self.alert_registers[index - 1]

        if limit is not None:
            alert_limit = self.alert_limits[index - 1]
            self._mcp9600.set(alert_limit, value=limit)

        if hysteresis is not None:
            alert_hysteresis = self.alert_hysteresis[index - 1]
            self._mcp9600.set(alert_hysteresis, value=hysteresis)

        self._mcp9600.set(alert_register,
                          clear_interrupt=1 if clear_interrupt else 0,
                          monitor_junction=monitor_junction,
                          rise_fall=rise_fall,
                          state=state,
                          mode=mode,
                          enable=1 if enable else 0)
Beispiel #6
0
class MSA301:
    def __init__(self, i2c_addr=0x26, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._is_setup = False
        # Device definition
        self._msa301 = Device([0x26], i2c_dev=self._i2c_dev, bit_width=8, registers=(
            Register('SOFT_RESET', 0x00, fields=(
                BitField('soft_reset', 0b00100000),
                BitField('seft_reset_alt', 0b00000100)
            )),
            Register('PARTID', 0x01, fields=(BitField('id', 0xFF),)),
            Register('ACCEL', 0x02, fields=(
                BitField('x', 0x00000000FFFF, adapter=SensorDataAdapter()),
                BitField('y', 0x0000FFFF0000, adapter=SensorDataAdapter()),
                BitField('z', 0xFFFF00000000, adapter=SensorDataAdapter()),
            ), bit_width=48, read_only=True),
            Register('MOTION_INTERRUPT', 0x09, fields=(
                BitField('interrupts', 0xFF, read_only=True, adapter=InterruptLookupAdapter({
                    'orientation_interrupt': 0b01000000,
                    'single_tap_interrupt': 0b00100000,
                    'double_tap_interrupt': 0b00010000,
                    'active_interrupt': 0b00000100,
                    'freefall_interrupt': 0b00000001
                })),
            )),
            Register('DATA_INTERRUPT', 0x0A, fields=(
                BitField('new_data_interrupt', 0b00000001, read_only=True),
            )),
            Register('TAP_ACTIVE_STATUS', 0x0B, fields=(
                BitField('tap_sign', 0b10000000, read_only=True),
                BitField('tap_triggered_x', 0b01000000, read_only=True),
                BitField('tap_triggered_y', 0b00100000, read_only=True),
                BitField('tap_triggered_z', 0b00010000, read_only=True),
                BitField('active_sign', 0b00001000, read_only=True),
                BitField('active_triggered_x', 0b00000100, read_only=True),
                BitField('active_triggered_y', 0b00000010, read_only=True),
                BitField('active_triggered_z', 0b00000001, read_only=True)
            )),
            Register('ORIENTATION_STATUS', 0x0C, fields=(
                BitField('z_orientation', 0b10000000, read_only=True),
                BitField('x_y_orientation', 0b01100000, read_only=True, adapter=LookupAdapter({
                    'portrait_upright': 0b00,
                    'portrait_upsidedown': 0b01,
                    'landscape_left': 0b10,
                    'landscape_right': 0b11
                }))
            )),
            Register('RESOLUTION_RANGE', 0x0F, fields=(
                BitField('resolution', 0b00001100, adapter=LookupAdapter({
                    14: 0b00,
                    12: 0b01,
                    10: 0b10,
                    8: 0b11
                }, snap=False)),
                BitField('range', 0b00000011, adapter=LookupAdapter({
                    2: 0b00,
                    4: 0b01,
                    8: 0b10,
                    16: 0b11
                }, snap=False))
            )),
            Register('DISABLE_AXIS_ODR', 0x10, fields=(
                BitField('disable_x_axis', 0b10000000),
                BitField('disable_y_axis', 0b01000000),
                BitField('disable_z_axis', 0b00100000),
                BitField('output_data_rate', 0b00001111, adapter=LookupAdapter({
                    '1Hz': 0b0000,     # Not Available in Normal Mode
                    '1.95Hz': 0b0001,  # Not Available in Normal Mode
                    '3.9Hz': 0b0010,
                    '7.81Hz': 0b0011,
                    '15.63Hz': 0b0100,
                    '31.25Hz': 0b0101,
                    '62.5Hz': 0b0110,
                    '125Hz': 0b0111,
                    '250Hz': 0b1000,
                    '500Hz': 0b1001,   # Not Available in Low Power Mode
                    '1000Hz': 0b1010   # Not Available in Low Power Mode
                }))
            )),
            Register('POWER_MODE_BANDWIDTH', 0x11, fields=(
                BitField('power_mode', 0b11000000, adapter=LookupAdapter({
                    'normal': 0b00,
                    'low_power': 0b01,
                    'suspend': 0b10
                })),
                BitField('low_power_bandwidth', 0b00011110, adapter=LookupAdapter({
                    '1.95Hz': 0b0010,
                    '3.9Hz': 0b0011,
                    '7.81Hz': 0b0100,
                    '15.63Hz': 0b0101,
                    '31.25Hz': 0b0110,
                    '62.5Hz': 0b0111,
                    '125Hz': 0b1000,
                    '250Hz': 0b1001,
                    '500Hz': 0b1010
                }))
            )),
            Register('SWAP_POLARITY', 0x12, fields=(
                BitField('x_polariy', 0b00001000),
                BitField('y_polariy', 0b00000100),
                BitField('z_polariy', 0b00000010),
                BitField('x_z_swap', 0b00000001)
            )),
            Register('INTERRUPT_ENABLE', 0x16, fields=(
                BitField('enable', 0xFF),
                BitField('interrupts', 0xFF, adapter=InterruptLookupAdapter({
                    'orientation_interrupt': 0b01000000,
                    'single_tap_interrupt': 0b00100000,
                    'double_tap_interrupt': 0b00010000,
                    'z_active_interrupt': 0b00000100,
                    'y_active_interrupt': 0b00000010,
                    'x_active_interrupt': 0b00000001
                })),
            )),
            Register('INTERRUPT_ENABLE_1', 0x17, fields=(
                BitField('enable', 0xFF),
                BitField('interrupts', 0xFF, adapter=InterruptLookupAdapter({
                    'data_interrupt': 0b00010000,
                    'freefall_interrupt': 0b00001000
                })),
            )),
            Register('INT1_MAPPING_0', 0x19, fields=(
                BitField('map_orientation_int1', 0b01000000),
                BitField('map_single_tap_int1', 0b00100000),
                BitField('map_double_tap_int1', 0b00010000),
                BitField('map_active_int1', 0b00000100),
                BitField('map_freefall_int1', 0b00000001)
            )),
            Register('INT1_MAPPING_1', 0x1A, fields=(
                [BitField('map_new_data_int1', 0b00000001)]
            )),
            Register('INT1_OUTPUT_MODE', 0x20, fields=(
                BitField('mode', 0b00000010, adapter=LookupAdapter({
                    'push_pull': 0b0,
                    'open_drain': 0b1
                })),
                BitField('active_level', 0b00000010, adapter=LookupAdapter({
                    'low': 0b0,
                    'high': 0b1
                })),
            )),
            Register('INT1_LATCH', 0x21, fields=(
                BitField('latch_reset', 0b10000000),
                BitField('latch_setting', 0b00001111, adapter=LookupAdapter({
                    'non_latched': 0b0000,
                    'temp_latch_250ms': 0b0001,
                    'temp_latch_500ms': 0b0010,
                    'temp_latch_1s': 0b0011,
                    'temp_latch_2s': 0b0100,
                    'temp_latch_4s': 0b0101,
                    'temp_latch_8s': 0b0110,
                    'latched': 0b0111,
                    'non_latched_': 0b1000,
                    'temp_latch_1ms': 0b1001,
                    'temp_latch_1ms_': 0b1010,  # TODO allow LookupAdapter to use list value
                    'temp_latch_2ms': 0b1011,
                    'temp_latch_25ms': 0b1100,
                    'temp_latch_50ms': 0b1101,
                    'temp_latch_100ms': 0b1110,
                    'latched_': 0b1111
                }))
            )),
            Register('FREEFALL_DURATION', 0x22, fields=(
                BitField('duration', 0xFF),
            )),
            Register('FREEFALL_THRESHOLD', 0x23, fields=(
                BitField('threshold', 0xFF),
            )),
            Register('FREEFALL_HYSTERESIS', 0x24, fields=(
                BitField('mode', 0x00000100, adapter=LookupAdapter({
                    'single': 0b0,
                    'sum': 0b1
                })),
                BitField('hysteresis_time', 0b00000011)
            )),
            Register('ACTIVE_DURATION', 0x27, fields=(
                BitField('duration', 0b00000011),
            )),
            Register('ACTIVE_THRESHOLD', 0x21, fields=(
                BitField('threshold', 0xFF),
            )),
            Register('TAP_DURATION', 0x28, fields=(
                BitField('tap_quiet', 0x10000000, adapter=LookupAdapter({
                    '30ms': 0b0,
                    '20ms': 0b1
                })),
                BitField('tap_shock', 0x01000000, adapter=LookupAdapter({
                    '50ms': 0b0,
                    '70ms': 0b1
                })),
                BitField('double_tap_window', 0b00000111, adapter=LookupAdapter({
                    '50ms': 0b000,
                    '100ms': 0b001,
                    '150ms': 0b010,
                    '200ms': 0b011,
                    '250ms': 0b100,
                    '375ms': 0b101,
                    '500ms': 0b110,
                    '700ms': 0b111
                }))
            )),
            Register('TAP_THRESHOLD', 0x2B, fields=(
                [BitField('threshold', 0b00011111)]
            )),
            Register('ORIENTATION_HYSTERESIS', 0x2C, fields=(
                BitField('hysteresis', 0b01110000),  # set the hysteresis of the orientation interrupt, 1LSB is 62.5mg
                BitField('orientation_blocking', 0b00001100, adapter=LookupAdapter({
                    'non_blocking': 0b00,
                    'z_axis_blocking': 0b01,
                    'z_axis_any_0.2_slope_blocking': 0b10,
                    'non_blocking_': 0b11  # TODO LookupAdapter needs list value
                })),
                BitField('orientation_mode', 0b00000011, adapter=LookupAdapter({
                    'symmetrical': 0b00,
                    'high_asymmetrical': 0b01,
                    'low_asymmetrical': 0b10,
                    'symmetrical_': 0b11   # TODO LookupAdapter needs list value
                }))
            )),
            Register('Z_BLOCK', 0x2D, fields=(
                BitField('setting', 0b00001111),  # defines the block acc_z between 0g to 0.9375g
            )),
            Register('OFFSET', 0x38, fields=(
                BitField('x', 0x0000FF),          # the offset compensation value for X axis, 1LSB is 3.9mg
                BitField('y', 0x00FF00),          # the offset compensation value for Y axis, 1LSB is 3.9mg
                BitField('z', 0xFF0000),          # the offset compensation value for Z axis, 1LSB is 3.9mg
            ), bit_width=24)
        ))

        res_range = self._msa301.get('RESOLUTION_RANGE')
        self._resolution = res_range.resolution
        self._range = res_range.range

    def twos_comp_conversion(self, val, bits=14):
        if (val & (1 << (bits - 1))) != 0:  # if sign bit is set e.g., 8bit: 128-255
            val = val - (1 << bits)         # compute negative value
        return val

    def twos_comp_to_bin(self, val):

        result = 0x00
        if val < 0:
            result = (1 << 7) + (128 + val)
        else:
            result = val
        return result

    def reset(self):
        self._msa301.set('SOFT_RESET', soft_reset=True)
        time.sleep(0.01)

    def get_part_id(self):
        return self._msa301.get('PARTID').id

    def set_range(self, value):
        valid_ranges = [2, 4, 8, 16]
        if (value in valid_ranges):
            self._range = value
            self._msa301.set('RESOLUTION_RANGE', range=value)
        else:
            raise ValueError('Invalid range value {}'.format(value))

    def set_resolution(self, resolution):
        valid_resolutions = [14, 12, 10, 8]
        if (resolution in valid_resolutions):
            self._resolution = resolution
            self._msa301.set('RESOLUTION_RANGE', resolution=resolution)
        else:
            raise ValueError('Invalid resolution Value {}'.format(resolution))

    def get_current_range_and_resolution(self):
        return self._range, self._resolution

    def set_output_data_rate(self, rate):
        self._msa301.set('DISABLE_AXIS_ODR', output_data_rate=rate)

    def get_output_data_rate(self):
        return self._msa301.get('DISABLE_AXIS_ODR').output_data_rate

    def get_power_mode(self):
        return self._msa301.get('POWER_MODE_BANDWIDTH').power_mode

    def set_power_mode(self, mode):
        self._msa301.set('POWER_MODE_BANDWIDTH', power_mode=mode)

    def get_interrupt(self):
        return self._msa301.get('INTERRUPT_ENABLE').interrupts + self._msa301.get('INTERRUPT_ENABLE_1').interrupts

    def enable_interrupt(self, interrupts):
        enable_1_interrupts = []
        for interrupt_item in interrupts:
            if interrupt_item == 'data_interrupt':
                interrupt_list_index = interrupts.index('data_interrupt')
                enable_1_interrupts.append(interrupts.pop(interrupt_list_index))

            if interrupt_item == 'freefall_interrupt':
                interrupt_list_index = interrupts.index('freefall_interrupt')
                enable_1_interrupts.append(interrupts.pop(interrupt_list_index))

        self._msa301.set('INTERRUPT_ENABLE', interrupts=interrupts)
        self._msa301.set('INTERRUPT_ENABLE_1', interrupts=enable_1_interrupts)

    def wait_for_interrupt(self, interrupts, polling_delay=0.01):
        check_interupts_enabled = self.get_interrupt()
        activity_interrupt_enable_flags = ['z_active_interrupt',
                                           'y_active_interrupt',
                                           'x_active_interrupt']

        if interrupts == 'active_interrupt':
            flag_detected = False

            for flag in activity_interrupt_enable_flags:
                if flag in check_interupts_enabled:
                    flag_detected = True

            if flag_detected is False:
                raise RuntimeError('{0} not Enabled!'.format(interrupts))

        elif interrupts not in check_interupts_enabled:
            raise RuntimeError('{0} not Enabled!'.format(interrupts))

        triggered_interrupt = [None]

        while interrupts not in triggered_interrupt:
            triggered_interrupt = self._msa301.get('MOTION_INTERRUPT').interrupts

            if interrupts in triggered_interrupt:
                return triggered_interrupt

            time.sleep(polling_delay)

        return triggered_interrupt

    def disable_all_interrupts(self):
        # TODO Verify that this works!
        self._msa301.set('INTERRUPT_ENABLE', enable=0x00)
        self._msa301.set('INTERRUPT_ENABLE_1', enable=0x00)

    def get_raw_measurements(self):
        accel = self._msa301.get('ACCEL')
        x = self.twos_comp_conversion(accel.x)
        y = self.twos_comp_conversion(accel.y)
        z = self.twos_comp_conversion(accel.z)

        return x, y, z

    def get_measurements(self):
        accel = self._msa301.get('ACCEL')
        x = float(self.twos_comp_conversion(accel.x, 14)) * (self._range / 8192.0)
        y = float(self.twos_comp_conversion(accel.y, 14)) * (self._range / 8192.0)
        z = float(self.twos_comp_conversion(accel.z, 14)) * (self._range / 8192.0)

        return x, y, z

    def get_raw_offsets(self):
        offset = self._msa301.get('OFFSET')

        return offset.x, offset.y, offset.z

    def set_raw_offsets(self, offset_x=0, offset_y=0, offset_z=0):
        if (-128 <= offset_z <= 128 and -128 <= offset_y <= 128 and -128 <= offset_x <= 128):
            self._msa301.set('OFFSET', x=offset_x, y=offset_y, z=offset_z)

        else:
            raise ValueError('Offset must be between -128 to 128')

    def get_offsets(self):
        x, y, z = self.get_raw_offsets()

        offset_x = -self.twos_comp_conversion(x, 8) * 0.0039
        offset_y = -self.twos_comp_conversion(y, 8) * 0.0039
        offset_z = -self.twos_comp_conversion(z, 8) * 0.0039

        return offset_x, offset_y, offset_z

    def set_offsets(self, offset_x=0, offset_y=0, offset_z=0):
        if (-0.5 <= offset_z <= 0.5 and -0.5 <= offset_y <= 0.5 and -0.5 <= offset_x <= 0.5):
            x = self.twos_comp_to_bin(int(-offset_x / 0.0039))
            y = self.twos_comp_to_bin(int(-offset_y / 0.0039))
            z = self.twos_comp_to_bin(int(-offset_z / 0.0039))
            self.set_raw_offsets(x, y, z)
        else:
            raise ValueError('Offset must be between -0.5g to 0.5g')
Beispiel #7
0
class TCS3472:
    def __init__(self, i2c_dev=None):
        """Initialise the TCS3472."""

        self._max_count = 0
        self._integration_time_ms = 0
        self._rgbc_tuple = namedtuple('Colour', (
            'time',
            'red',
            'green',
            'blue',
            'raw_red',
            'raw_green',
            'raw_blue',
            'raw_clear'
        ))

        self._tcs3472 = Device(I2C_ADDR, i2c_dev=i2c_dev, bit_width=8, registers=(
            Register('ENABLE', I2C_COMMAND | 0x00, fields=(
                BitField('power_on', 0b00000001),
                BitField('enable', 0b00000010),
                BitField('wait_enable', 0b00001000),
                BitField('int_enable', 0b00010000)
            )),
            # Actual integration time is (256 - INTEGRATION_TIME) * 2.4
            # Integration time affects the max ADC count, with the max count
            # being (256 - INTEGRATION_TIME) * 1024 up to a limit of 65535.
            # IE: An integration time of 0xF6 (24ms) would limit readings to 0-10240.
            Register('INTEGRATION_TIME', I2C_COMMAND | 0x01, fields=(
                BitField('time_ms', 0xff, adapter=IntegrationTimeAdapter()),  
            )),
            # Actual wait time is (256 - WAIT_TIME) * 2.4
            # if the WLONG bit is set, this value is multiplied by 12
            Register('WAIT_TIME', I2C_COMMAND | 0x03, fields=(
                BitField('time_ms', 0xff, adapter=WaitTimeAdapter()),
            )),
            Register('INTERRUPT_THRESHOLD', I2C_COMMAND | I2C_AUTOINC | 0x04, fields=(
                BitField('low', 0x0000ffff),
                BitField('high', 0xffff0000)
            ), bit_width=8 * 4),
            Register('PERSISTENCE', I2C_COMMAND | 0x0c, fields=(
                BitField('count', 0x0f, adapter=LookupAdapter({
                    0: 0b0000,
                    1: 0b0001,
                    2: 0b0010,
                    3: 0b0011,
                    5: 0b0100,
                    10: 0b0101,
                    15: 0b0110,
                    20: 0b0111,
                    25: 0b1000,
                    30: 0b1001,
                    35: 0b1010,
                    40: 0b1011,
                    45: 0b1100,
                    50: 0b1101,
                    55: 0b1110,
                    60: 0b1111
                })),
            )),
            # wlong multiplies the wait time by 12
            Register('CONFIGURATION', I2C_COMMAND | 0x0d, fields=(
                BitField('wlong', 0b00000010),    
            )),
            Register('CONTROL', I2C_COMMAND | 0x0f, fields=(
                BitField('gain', 0b00000011, adapter=LookupAdapter({
                    1: 0b00,
                    4: 0b01,
                    16: 0b10,
                    60: 0b11
                })),   
            )),
            # Should be either 0x44 (TCS34725) or 0x4D (TCS34727)
            Register('ID', I2C_COMMAND | 0x12, fields=(
                BitField('id', 0xff),    
            )),
            Register('STATUS', I2C_COMMAND | 0x13, fields=(
                BitField('aint', 0b00010000),
                BitField('avalid', 0b00000001)
            )),
            Register('RGBC', I2C_COMMAND | I2C_AUTOINC | 0x14, fields=(
                BitField('clear', 0xffff, adapter=U16ByteSwapAdapter()),
                BitField('red', 0xffff << 16, adapter=U16ByteSwapAdapter()),
                BitField('green', 0xffff << 32, adapter=U16ByteSwapAdapter()),
                BitField('blue', 0xffff << 48, adapter=U16ByteSwapAdapter())
            ), bit_width=8 * 8)
        ))

        chip = self._tcs3472.get('ID')

        if chip.id not in CHIP_ID:
            raise RuntimeError("TCS3472 not found! Chip ID {} not in {}".format(chip.id, CHIP_ID))

        # Enable the sensor and RGBC interface by default
        self._tcs3472.set('ENABLE', power_on=True, enable=True)

        # Aim for 100ms integration time, or 10 readings / second
        # This should give a saturation of ~41984 counts: (256 - 0xd7) * 1024
        # During testing it reached saturation at 41984
        self.set_integration_time_ms(100)
    
    def set_wait_time_ms(self, value, wait_long=False):
        self._tcs3472.set('WAIT_TIME', time_ms=value)
        self._tcs3472.set('CONFIGURATION', wlong=wait_long)

    def set_integration_time_ms(self, value):
        """Set the sensor integration time in milliseconds.
        
        :param value: Time in milliseconds from 0 to 614.
        
        """
        self._tcs3472.set('INTEGRATION_TIME', time_ms=value)
        # Calculate the max ADC count using the converted integration time.
        # This is used to scale ADC readings.
        self._max_count = int((256 - IntegrationTimeAdapter()._encode(value)) * 1024)
        self._max_count = min(65535, self._max_count)
        self._integration_time_ms = value

    def get_rgbc_counts(self):
        while not self._tcs3472.get('STATUS').avalid:
            time.sleep(0.0001)
        return self._tcs3472.get('RGBC')

    def get_rgbc(self):
        rgbc = self.get_rgbc_counts()

        scale = max(rgbc)

        try:
            return self._rgbc_tuple(
                time.time(),
                int((float(rgbc.red) / scale) * 255),
                int((float(rgbc.green) / scale) * 255),
                int((float(rgbc.blue) / scale) * 255),
                float(rgbc.red) / self._max_count,
                float(rgbc.green) / self._max_count,
                float(rgbc.blue) / self._max_count,
                float(rgbc.clear) / self._max_count
            )
        except ZeroDivisionError:
            return self._rgbc_tuple(
                time.time(),
                0,
                0,
                0,
                float(rgbc.red) / self._max_count,
                float(rgbc.green) / self._max_count,
                float(rgbc.blue) / self._max_count,
                float(rgbc.clear) / self._max_count
            )
Beispiel #8
0
class BME280:
    def __init__(self, i2c_addr=I2C_ADDRESS_GND, i2c_dev=None):
        self.calibration = BME280Calibration()
        self._is_setup = False
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._bme280 = Device(
            [I2C_ADDRESS_GND, I2C_ADDRESS_VCC],
            i2c_dev=self._i2c_dev,
            bit_width=8,
            registers=(
                Register('CHIP_ID', 0xD0, fields=(BitField('id', 0xFF), )),
                Register('RESET', 0xE0, fields=(BitField('reset', 0xFF), )),
                Register(
                    'STATUS',
                    0xF3,
                    fields=(
                        BitField('measuring',
                                 0b00001000),  # 1 when conversion is running
                        BitField(
                            'im_update',
                            0b00000001),  # 1 when NVM data is being copied
                    )),
                Register(
                    'CTRL_MEAS',
                    0xF4,
                    fields=(
                        BitField(
                            'osrs_t',
                            0b11100000,  # Temperature oversampling
                            adapter=LookupAdapter({
                                1: 0b001,
                                2: 0b010,
                                4: 0b011,
                                8: 0b100,
                                16: 0b101
                            })),
                        BitField(
                            'osrs_p',
                            0b00011100,  # Pressure oversampling
                            adapter=LookupAdapter({
                                1: 0b001,
                                2: 0b010,
                                4: 0b011,
                                8: 0b100,
                                16: 0b101
                            })),
                        BitField(
                            'mode',
                            0b00000011,  # Power mode
                            adapter=LookupAdapter({
                                'sleep': 0b00,
                                'forced': 0b10,
                                'normal': 0b11
                            })),
                    )),
                Register(
                    'CTRL_HUM',
                    0xF2,
                    fields=(
                        BitField(
                            'osrs_h',
                            0b00000111,  # Humidity oversampling
                            adapter=LookupAdapter({
                                1: 0b001,
                                2: 0b010,
                                4: 0b011,
                                8: 0b100,
                                16: 0b101
                            })), )),
                Register(
                    'CONFIG',
                    0xF5,
                    fields=(
                        BitField(
                            't_sb',
                            0b11100000,  # Temp standby duration in normal mode
                            adapter=LookupAdapter({
                                0.5: 0b000,
                                62.5: 0b001,
                                125: 0b010,
                                250: 0b011,
                                500: 0b100,
                                1000: 0b101,
                                10: 0b110,
                                20: 0b111
                            })),
                        BitField(
                            'filter', 0b00011100
                        ),  # Controls the time constant of the IIR filter
                        BitField(
                            'spi3w_en', 0b0000001, read_only=True
                        ),  # Enable 3-wire SPI interface when set to 1. IE: Don't set this bit!
                    )),
                Register('DATA',
                         0xF7,
                         fields=(BitField('humidity', 0x000000000000FFFF),
                                 BitField('temperature', 0x000000FFFFF00000),
                                 BitField('pressure', 0xFFFFF00000000000)),
                         bit_width=8 * 8),
                Register(
                    'CALIBRATION',
                    0x88,
                    fields=(
                        BitField('dig_t1',
                                 0xFFFF << 16 * 12,
                                 adapter=U16Adapter()),  # 0x88 0x89
                        BitField('dig_t2',
                                 0xFFFF << 16 * 11,
                                 adapter=S16Adapter()),  # 0x8A 0x8B
                        BitField('dig_t3',
                                 0xFFFF << 16 * 10,
                                 adapter=S16Adapter()),  # 0x8C 0x8D
                        BitField('dig_p1',
                                 0xFFFF << 16 * 9,
                                 adapter=U16Adapter()),  # 0x8E 0x8F
                        BitField('dig_p2',
                                 0xFFFF << 16 * 8,
                                 adapter=S16Adapter()),  # 0x90 0x91
                        BitField('dig_p3',
                                 0xFFFF << 16 * 7,
                                 adapter=S16Adapter()),  # 0x92 0x93
                        BitField('dig_p4',
                                 0xFFFF << 16 * 6,
                                 adapter=S16Adapter()),  # 0x94 0x95
                        BitField('dig_p5',
                                 0xFFFF << 16 * 5,
                                 adapter=S16Adapter()),  # 0x96 0x97
                        BitField('dig_p6',
                                 0xFFFF << 16 * 4,
                                 adapter=S16Adapter()),  # 0x98 0x99
                        BitField('dig_p7',
                                 0xFFFF << 16 * 3,
                                 adapter=S16Adapter()),  # 0x9A 0x9B
                        BitField('dig_p8',
                                 0xFFFF << 16 * 2,
                                 adapter=S16Adapter()),  # 0x9C 0x9D
                        BitField('dig_p9',
                                 0xFFFF << 16 * 1,
                                 adapter=S16Adapter()),  # 0x9E 0x9F
                        BitField('dig_h1', 0x00FF),  # 0xA1 uint8
                    ),
                    bit_width=26 * 8),
                Register(
                    'CALIBRATION2',
                    0xE1,
                    fields=(
                        BitField('dig_h2',
                                 0xFFFF0000000000,
                                 adapter=S16Adapter()),  # 0xE1 0xE2
                        BitField('dig_h3', 0x0000FF00000000),  # 0xE3 uint8
                        BitField('dig_h4',
                                 0x000000FFFF0000,
                                 adapter=H4Adapter()),  # 0xE4 0xE5[3:0]
                        BitField('dig_h5',
                                 0x00000000FFFF00,
                                 adapter=H5Adapter()),  # 0xE5[7:4] 0xE6
                        BitField('dig_h6',
                                 0x000000000000FF,
                                 adapter=S8Adapter())  # 0xE7 int8
                    ),
                    bit_width=7 * 8)))

    def setup(self,
              mode='normal',
              temperature_oversampling=16,
              pressure_oversampling=16,
              humidity_oversampling=16,
              temperature_standby=500):
        if self._is_setup:
            return
        self._is_setup = True

        self._bme280.select_address(self._i2c_addr)
        self._mode = mode

        if mode == "forced":
            mode = "sleep"

        #try:
        #    chip = self._bme280.get('CHIP_ID')
        #    if chip.id != CHIP_ID:
        #        raise RuntimeError("Unable to find bme280 on 0x{:02x}, CHIP_ID returned {:02x}".format(self._i2c_addr, chip.id))
        #except IOError:
        #    raise RuntimeError("Unable to find bme280 on 0x{:02x}, IOError".format(self._i2c_addr))

        self._bme280.set('RESET', reset=0xB6)
        time.sleep(0.1)

        self._bme280.set('CTRL_HUM', osrs_h=humidity_oversampling)

        self._bme280.set('CTRL_MEAS',
                         mode=mode,
                         osrs_t=temperature_oversampling,
                         osrs_p=pressure_oversampling)

        self._bme280.set('CONFIG', t_sb=temperature_standby, filter=2)

        self.calibration.set_from_namedtuple(self._bme280.get('CALIBRATION'))
        self.calibration.set_from_namedtuple(self._bme280.get('CALIBRATION2'))

    def update_sensor(self):
        self.setup()

        if self._mode == "forced":
            self._bme280.set('CTRL_MEAS', mode="forced")
            while self._bme280.get('STATUS').measuring:
                time.sleep(0.001)

        raw = self._bme280.get('DATA')
        self.temperature = self.calibration.compensate_temperature(
            raw.temperature)
        self.pressure = self.calibration.compensate_pressure(
            raw.pressure) / 100.0
        self.humidity = self.calibration.compensate_humidity(raw.humidity)

    def get_temperature(self):
        self.update_sensor()
        return self.temperature

    def get_pressure(self):
        self.update_sensor()
        return self.pressure

    def get_humidity(self):
        self.update_sensor()
        return self.humidity

    def get_altitude(self, qnh=1017):
        self.update_sensor()
        self.altitude = 44330.0 * (1.0 - pow(self.pressure / qnh,
                                             (1.0 / 5.255)))
        return self.altitude

    def all(self, qnh=1017):
        self.update_sensor()
        self.altitude = 44330.0 * (1.0 - pow(self.pressure / qnh,
                                             (1.0 / 5.255)))
        return self.temperature, self.pressure, self.humidity, self.altitude
Beispiel #9
0
class LSM303D:
    def __init__(self, i2c_dev, i2c_addr=0x1D):
        self._i2c_addr = i2c_addr  # device address
        self._i2c_dev = SMBus(i2c_dev)  # i2c bus object
        self._lsm303d = Device(
            self._i2c_dev,
            [0x1D, 0x1E],
            bit_width=8,
            registers=(
                Register('TEMPERATURE',
                         0x05 | 0x80,
                         fields=(BitField('temperature',
                                          0xFFFF,
                                          adapter=TemperatureAdapter()), ),
                         bit_width=16),

                # Magnetometer interrupt status
                Register('MAGNETOMETER_STATUS',
                         0x07,
                         fields=(
                             BitField('xdata', 0b00000001),
                             BitField('ydata', 0b00000010),
                             BitField('zdata', 0b00000100),
                             BitField('data', 0b00001000),
                             BitField('xoverrun', 0b00010000),
                             BitField('yoverrun', 0b00100000),
                             BitField('zoverrun', 0b01000000),
                             BitField('overrun', 0b10000000),
                         )),
                Register('MAGNETOMETER',
                         0x08 | 0x80,
                         fields=(
                             BitField('x',
                                      0xFFFF00000000,
                                      adapter=S16ByteSwapAdapter()),
                             BitField('y',
                                      0x0000FFFF0000,
                                      adapter=S16ByteSwapAdapter()),
                             BitField('z',
                                      0x00000000FFFF,
                                      adapter=S16ByteSwapAdapter()),
                         ),
                         bit_width=8 * 6),
                Register('WHOAMI', 0x0F, fields=(BitField('id', 0xFF), )),
                Register(
                    'MAGNETOMETER_INTERRUPT',
                    0x12,
                    fields=(
                        BitField('enable', 0b00000001),
                        BitField('enable_4d', 0b00000010),
                        BitField('latch', 0b00000100),
                        BitField(
                            'polarity',
                            0b00001000),  # 0 = active-low, 1 = active-high
                        BitField('pin_config',
                                 0b00010000),  # 0 = push-pull, 1 = open-drain
                        BitField('z_enable', 0b00100000),
                        BitField('y_enable', 0b01000000),
                        BitField('x_enable', 0b10000000),
                    )),
                Register('MAGNETOMETER_INTERRUPT_SOURCE',
                         0x13,
                         fields=(
                             BitField('event', 0b00000001),
                             BitField('overflow', 0b00000010),
                             BitField('z_negative', 0b00000100),
                             BitField('y_negative', 0b00001000),
                             BitField('x_negative', 0b00010000),
                             BitField('z_positive', 0b00100000),
                             BitField('y_positive', 0b01000000),
                             BitField('x_positive', 0b10000000),
                         )),
                Register('MAGNETOMETER_INTERRUPT_THRESHOLD',
                         0x14 | 0x80,
                         fields=(BitField('threshold',
                                          0xFFFF,
                                          adapter=U16ByteSwapAdapter()), ),
                         bit_width=16),
                Register('MAGNETOMETER_OFFSET',
                         0x16 | 0x80,
                         fields=(
                             BitField('x',
                                      0xFFFF00000000,
                                      adapter=S16ByteSwapAdapter()),
                             BitField('y',
                                      0x0000FFFF0000,
                                      adapter=S16ByteSwapAdapter()),
                             BitField('z',
                                      0x00000000FFFF,
                                      adapter=S16ByteSwapAdapter()),
                         ),
                         bit_width=8 * 6),
                Register('HP_ACCELEROMETER_REFERENCE',
                         0x1c | 0x80,
                         fields=(
                             BitField('x', 0xFF0000),
                             BitField('y', 0x00FF00),
                             BitField('z', 0x0000FF),
                         ),
                         bit_width=8 * 3),
                Register('CONTROL0',
                         0x1f,
                         fields=(
                             BitField('int2_high_pass', 0b00000001),
                             BitField('int1_high_pass', 0b00000010),
                             BitField('click_high_pass', 0b00000100),
                             BitField('fifo_threshold', 0b00100000),
                             BitField('fifo_enable', 0b01000000),
                             BitField('reboot_memory', 0b10000000),
                         )),
                Register('CONTROL1',
                         0x20,
                         fields=(
                             BitField('accel_x_enable', 0b00000001),
                             BitField('accel_y_enable', 0b00000010),
                             BitField('accel_z_enable', 0b00000100),
                             BitField('block_data_update', 0b00001000),
                             BitField('accel_data_rate_hz',
                                      0b11110000,
                                      adapter=LookupAdapter({
                                          0: 0,
                                          3.125: 0b0001,
                                          6.25: 0b0010,
                                          12.5: 0b0011,
                                          25: 0b0100,
                                          50: 0b0101,
                                          100: 0b0110,
                                          200: 0b0111,
                                          400: 0b1000,
                                          800: 0b1001,
                                          1600: 0b1010
                                      })),
                         )),
                Register('CONTROL2',
                         0x21,
                         fields=(
                             BitField('serial_interface_mode', 0b00000001),
                             BitField('accel_self_test', 0b00000010),
                             BitField('accel_full_scale_g',
                                      0b00111000,
                                      adapter=LookupAdapter({
                                          2: 0b000,
                                          4: 0b001,
                                          6: 0b010,
                                          8: 0b011,
                                          16: 0b100
                                      })),
                             BitField('accel_antialias_bw_hz',
                                      0b11000000,
                                      adapter=LookupAdapter({
                                          50: 0b11,
                                          362: 0b10,
                                          194: 0b01,
                                          773: 0b00
                                      })),
                         )),

                # Known in the datasheet as CTRL3
                Register('INTERRUPT1',
                         0x22,
                         fields=(
                             BitField('enable_fifo_empty', 0b00000001),
                             BitField('enable_accel_dataready', 0b00000010),
                             BitField('enable_accelerometer', 0b00000100),
                             BitField('enable_magnetometer', 0b00001000),
                             BitField('enable_ig2', 0b00010000),
                             BitField('enable_ig1', 0b00100000),
                             BitField('enable_click', 0b01000000),
                             BitField('enable_boot', 0b10000000),
                         )),

                # Known in the datasheet as CTRL4
                Register('INTERRUPT2',
                         0x23,
                         fields=(
                             BitField('enable_fifo', 0b00000001),
                             BitField('enable_fifo_overrun', 0b00000010),
                             BitField('enable_mag_dataready', 0b00000100),
                             BitField('enable_accel_dataready', 0b00001000),
                             BitField('enable_magnetometer', 0b00010000),
                             BitField('enable_ig2', 0b00100000),
                             BitField('enable_ig1', 0b01000000),
                             BitField('enable_click', 0b10000000),
                         )),
                Register('CONTROL5',
                         0x24,
                         fields=(
                             BitField('latch_int1', 0b00000001),
                             BitField('latch_int2', 0b00000010),
                             BitField('mag_data_rate_hz',
                                      0b00011100,
                                      adapter=LookupAdapter({
                                          3.125: 0b000,
                                          6.25: 0b001,
                                          12.5: 0b010,
                                          25: 0b011,
                                          50: 0b100,
                                          100: 0b101,
                                      })),
                             BitField('mag_resolution', 0b01100000),
                             BitField('enable_temperature', 0b10000000),
                         )),
                Register('CONTROL6',
                         0x25,
                         fields=(BitField('mag_full_scale_gauss',
                                          0b01100000,
                                          adapter=LookupAdapter({
                                              2: 0b00,
                                              4: 0b01,
                                              8: 0b10,
                                              12: 0b11
                                          })), )),
                Register(
                    'CONTROL7',
                    0x26,
                    fields=(
                        BitField('mag_mode',
                                 0b00000011,
                                 adapter=LookupAdapter({
                                     'continuous': 0b00,
                                     'single': 0b01,
                                     'off': 0b10
                                 })),
                        BitField('mag_lowpowermode', 0b00000100),
                        BitField('temperature_only', 0b00010000),
                        BitField('filter_accel', 0b00100000),
                        BitField('high_pass_mode_accel',
                                 0b11000000),  # See page 39 of lsm303d.pdf
                    )),

                # Accelerometer interrupt status register
                Register('ACCELEROMETER_STATUS',
                         0x27,
                         fields=(BitField('xdata', 0b00000001),
                                 BitField('ydata', 0b00000010),
                                 BitField('zdata', 0b00000100),
                                 BitField('data', 0b00001000),
                                 BitField('xoverrun', 0b00010000),
                                 BitField('yoverrun', 0b00100000),
                                 BitField('zoverrun', 0b01000000),
                                 BitField('overrun', 0b10000000))),

                # X/Y/Z values from accelerometer
                Register('ACCELEROMETER',
                         0x28 | 0x80,
                         fields=(
                             BitField('x',
                                      0xFFFF00000000,
                                      adapter=S16ByteSwapAdapter()),
                             BitField('y',
                                      0x0000FFFF0000,
                                      adapter=S16ByteSwapAdapter()),
                             BitField('z',
                                      0x00000000FFFF,
                                      adapter=S16ByteSwapAdapter()),
                         ),
                         bit_width=8 * 6),

                # FIFO control register
                Register('FIFO_CONTROL',
                         0x2e,
                         fields=(
                             BitField('mode', 0b11100000),
                             BitField('threshold', 0b00011111),
                         )),

                # FIFO status register
                Register(
                    'FIFO_STATUS',
                    0x2f,
                    fields=(
                        BitField('threshold_exceeded', 1 << 7),
                        BitField('overrun', 1 << 6),
                        BitField('empty', 1 << 5),
                        BitField('unread_levels', 0b00011111
                                 ),  # Current number of unread FIFO levels
                    )),

                # 0x30: Internal interrupt generator 1: configuration register
                # 0x31: Internal interrupt generator 1: status register
                # 0x32: Internal interrupt generator 1: threshold register
                # 0x33: Internal interrupt generator 1: duration register
                Register(
                    'IG_CONFIG1',
                    0x30 | 0x80,
                    fields=(
                        # 0x30
                        BitField('and_or_combination', 1 << 31),
                        BitField('enable_6d', 1 << 30),
                        BitField('z_high_enable', 1 << 29),
                        BitField('z_low_enable', 1 << 28),
                        BitField('y_high_enable', 1 << 27),
                        BitField('y_low_enable', 1 << 26),
                        BitField('x_high_enable', 1 << 25),
                        BitField('x_low_enble', 1 << 24),
                        # 0x31
                        BitField('interrupt_status', 1 << 23),
                        BitField('z_high', 1 << 22),
                        BitField('z_low', 1 << 21),
                        BitField('y_high', 1 << 20),
                        BitField('y_low', 1 << 19),
                        BitField('x_high', 1 << 18),
                        BitField('x_low', 1 << 17),
                        BitField('status', 1 << 16),
                        # 0x32
                        BitField('threshold', 0xff << 8),
                        # 0x33
                        BitField('duration', 0xff),
                    ),
                    bit_width=32),

                # 0x34: Internal interrupt generator 2: configuration register
                # 0x35: Internal interrupt generator 2: status register
                # 0x36: Internal interrupt generator 2: threshold register
                # 0x37: Internal interrupt generator 2: duration register
                Register(
                    'IG_CONFIG1',
                    0x30 | 0x80,
                    fields=(
                        # 0x34
                        BitField('and_or_combination', 1 << 31),
                        BitField('enable_6d', 1 << 30),
                        BitField('z_high_enable', 1 << 29),
                        BitField('z_low_enable', 1 << 28),
                        BitField('y_high_enable', 1 << 27),
                        BitField('y_low_enable', 1 << 26),
                        BitField('x_high_enable', 1 << 25),
                        BitField('x_low_enble', 1 << 24),
                        # 0x35
                        BitField('interrupt_status', 1 << 23),
                        BitField('z_high', 1 << 22),
                        BitField('z_low', 1 << 21),
                        BitField('y_high', 1 << 20),
                        BitField('y_low', 1 << 19),
                        BitField('x_high', 1 << 18),
                        BitField('x_low', 1 << 17),
                        BitField('status', 1 << 16),
                        # 0x36
                        BitField('threshold', 0xff << 8),
                        # 0x37
                        BitField('duration', 0xff),
                    ),
                    bit_width=32),

                # 0x38: Click: configuration register
                # 0x39: Click: status register
                # 0x3A: Click: threshold register
                # 0x3B: Click: time limit register
                # 0x3C: Click: time latency register
                # 0x3D: Click: time window register
                Register(
                    'CLICK',
                    0x38 | 0x80,
                    fields=(
                        # 0x38
                        # bits 1 << 47 and 1 << 46 are unimplemented
                        BitField('z_doubleclick_enable', 1 << 45),
                        BitField('z_click_enable', 1 << 44),
                        BitField('y_doubleclick_enable', 1 << 43),
                        BitField('y_click_enable', 1 << 42),
                        BitField('x_doubleclick_enable', 1 << 41),
                        BitField('x_click_enable', 1 << 40),
                        # 0x39
                        # bit 1 << 39 is unimplemented
                        BitField('interrupt_enable', 1 << 38),
                        BitField('doubleclick_enable', 1 << 37),
                        BitField('click_enable', 1 << 36),
                        BitField(
                            'sign', 1 <<
                            35),  # 0 positive detection, 1 negative detection
                        BitField('z', 1 << 34),
                        BitField('y', 1 << 33),
                        BitField('x', 1 << 32),
                        # 0x3A
                        BitField('threshod', 0xFF << 24),
                        # 0x3B
                        BitField('time_limit', 0xFF << 16),
                        # 0x3C
                        BitField('time_latency', 0xFF << 8),
                        # 0x3D
                        BitField('time_window', 0xFF),
                    ),
                    bit_width=8 * 6),

                # Controls the threshold and duration of returning to sleep mode
                Register(
                    'ACT',
                    0x3e | 0x80,
                    fields=(
                        BitField('threshold', 0xFF00),  # 1 LSb = 16mg
                        BitField('duration',
                                 0x00FF)  # (duration + 1) * 8/output_data_rate
                    ),
                    bit_width=16)))

        self._is_setup = False

        self._accel_full_scale_g = 2
        self._mag_full_scale_guass = 2

    def set_accel_full_scale_g(self, scale):
        """Set the full scale range for the accelerometer in g

        :param scale: One of 2, 4, 6, 8 or 16 g

        """
        self._accel_full_scale_g = scale
        self._lsm303d.set('CONTROL2',
                          accel_full_scale_g=self._accel_full_scale_g)

    def set_mag_full_scale_guass(self, scale):
        """Set the full scale range for the magnetometer in guass

        :param scale: One of 2, 4, 8 or 12 guass

        """
        self._mag_full_scale_guass = scale
        self._lsm303d.set('CONTROL6', mag_full_scale_gauss=scale)  # +-2

    def setup(self):
        if self._is_setup:
            return
        self._is_setup = True

        self._lsm303d.select_address(self._i2c_addr)

        try:
            chip = self._lsm303d.get('WHOAMI')
            if chip.id != 0x49:
                raise RuntimeError(
                    "Unable to find lsm303d on 0x{:02x}, WHOAMI returned {:02x}"
                    .format(self._i2c_addr, chip.id))
        except IOError:
            raise RuntimeError(
                "Unable to find lsm303d on 0x{:02x}, IOError".format(
                    self._i2c_addr))

        self._lsm303d.set('CONTROL1',
                          accel_x_enable=1,
                          accel_y_enable=1,
                          accel_z_enable=1,
                          accel_data_rate_hz=50)

        self.set_accel_full_scale_g(2)

        self._lsm303d.set('INTERRUPT1',
                          enable_fifo_empty=0,
                          enable_accel_dataready=0,
                          enable_accelerometer=0,
                          enable_magnetometer=0,
                          enable_ig2=0,
                          enable_ig1=0,
                          enable_click=0,
                          enable_boot=0)

        self._lsm303d.set('INTERRUPT2',
                          enable_fifo=0,
                          enable_fifo_overrun=0,
                          enable_mag_dataready=0,
                          enable_accel_dataready=0,
                          enable_magnetometer=0,
                          enable_ig2=0,
                          enable_ig1=0,
                          enable_click=0)

        self._lsm303d.set('CONTROL5',
                          mag_data_rate_hz=50,
                          enable_temperature=1)

        self.set_mag_full_scale_guass(2)

        self._lsm303d.set('CONTROL7', mag_mode='continuous')

    def magnetometer(self):
        """Return magnetometer x, y and z readings.

        These readings are given in guass and should be +/- the given mag_full_scale_guass value.

        """
        self.setup()
        mag = self._lsm303d.get('MAGNETOMETER')
        x, y, z = mag.x, mag.y, mag.z
        x, y, z = [(p / 32676.0) * self._mag_full_scale_guass
                   for p in (x, y, z)]
        return x, y, z

    def accelerometer(self):
        """Return acelerometer x, y and z readings.

        These readings are given in g annd should be +/- the given accel_full_scale_g value.

        """
        self.setup()
        accel = self._lsm303d.get('ACCELEROMETER')
        x, y, z = accel.x, accel.y, accel.z
        x, y, z = [(p / 32767.0) * self._accel_full_scale_g for p in (x, y, z)]
        return x, y, z

    def temperature(self):
        """Return the temperature"""
        self.setup()
        return self._lsm303d.get('TEMPERATURE').temperature
Beispiel #10
0
class DRV2605():
    def __init__(self, i2c_addr=DRV2605_ADDR, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._drv2605 = Device(
            DRV2605_ADDR,
            i2c_dev=self._i2c_dev,
            registers=(
                Register('STATUS',
                         0x00,
                         fields=(
                             BitField('device_id', 0b11100000),
                             BitField('diagnostic', 0b00001000),
                             BitField('over_temp', 0b00000010),
                             BitField('over_current', 0b00000001),
                         )),
                Register(
                    'MODE',
                    0x01,
                    fields=(
                        BitField('reset', 0b10000000),
                        BitField('standby', 0b01000000),
                        BitField(
                            'mode',
                            0b00000111,
                            adapter=LookupAdapter({
                                'Internal Trigger':
                                0,  # Waveforms are fired by setting the GO bit in register 0x0C
                                'Edge Trigger':
                                1,  # A rising edge on INT/TRIG sets the GO bit, a second rising edge cancels the waveform
                                'Level Trigger':
                                2,  # A rising edge on INT/TRIG sets the GO bit, a falling edge cancels the waveform
                                'PWM/Analog In':
                                3,  # A PWM or Analog signal is accepted on INT/TRIG and used as a direct driving source
                                'Audio In':
                                4,  # An AC-coupled audio signal is accepted on INT/TRIG and turned into meaningful vibration
                                # (AC_COUPLE and N_PWM_ANALOG should also be set)
                                'Real-time Playback':
                                5,  # The contents of the REALTIME_PLAYBACK register are used as a waveform
                                'Diagnostics':
                                6,  # Perform a diagnostic test on the actuator- triggered by setting the GO bit
                                'Auto Calibration':
                                7  # Perform an auto-calibration- triggered by setting the GO bit
                            })),
                    )),
                Register('REALTIME_PLAYBACK',
                         0x02,
                         fields=(BitField('input', 0xFF), )),
                Register(
                    'LIBRARY_SELECTION',
                    0x03,
                    fields=(
                        BitField('high_impedance', 0b00010000),
                        BitField(
                            'library',
                            0b00000111,
                            adapter=LookupAdapter({
                                'Empty': 0,
                                'TS2200 A':
                                1,  # Rated 1.3v, Overdrive 3v, 40ms to 60ms rise, 20ms to 40ms brake
                                'TS2200 B':
                                2,  # Rated 3v, Overdrive 3v, 40ms to 60ms rise, 5ms to 15ms brake
                                'TS2200 C':
                                3,  # Rated 3v, Overdrive 3v, 60ms to 80ms rise, 10ms to 20ms brake
                                'TS2200 D':
                                4,  # Rated 3v, Overdrive 3v, 100ms to 140ms rise, 15ms to 25ms brake
                                'TS2200 E':
                                5,  # Rated 3v, Overdrive 3v, > 140ms rise, > 30ms brake
                                'LRA': 6,  # Linear Resonance
                                'TS2200 F':
                                7  # Rated 4.5v, Overdrive 5v, 35ms to 45ms rise, 10ms to 20ms brake
                            })),
                    )),
                # When the wait bit is set, the value of its corresponding
                # waveform becomes a timed delay.
                # Delay time = 10 ms x waveformN
                Register('WAVEFORM_SEQUENCER',
                         0x04,
                         fields=(
                             BitField('step1_wait',
                                      0xFF << 56,
                                      adapter=WaitTimeAdapter()),
                             BitField('step1_waveform', 0xFF << 56),
                             BitField('step2_wait',
                                      0xFF << 48,
                                      adapter=WaitTimeAdapter()),
                             BitField('step2_waveform', 0xFF << 48),
                             BitField('step3_wait',
                                      0xFF << 40,
                                      adapter=WaitTimeAdapter()),
                             BitField('step3_waveform', 0xFF << 40),
                             BitField('step4_wait',
                                      0xFF << 32,
                                      adapter=WaitTimeAdapter()),
                             BitField('step4_waveform', 0xFF << 32),
                             BitField('step5_wait',
                                      0xFF << 24,
                                      adapter=WaitTimeAdapter()),
                             BitField('step5_waveform', 0xFF << 24),
                             BitField('step6_wait',
                                      0xFF << 16,
                                      adapter=WaitTimeAdapter()),
                             BitField('step6_waveform', 0xFF << 16),
                             BitField('step7_wait',
                                      0xFF << 8,
                                      adapter=WaitTimeAdapter()),
                             BitField('step7_waveform', 0xFF << 8),
                             BitField('step8_wait',
                                      0xFF << 0,
                                      adapter=WaitTimeAdapter()),
                             BitField('step8_waveform', 0xFF << 0),
                         ),
                         bit_width=8 * 8),
                Register('GO', 0x0C, fields=(BitField('go', 0b00000001), )),
                Register('TIME_OFFSET',
                         0x0D,
                         fields=(BitField('overdrive', 0xFF000000),
                                 BitField('positive_sustain', 0x00FF0000),
                                 BitField('negative_sustain', 0x0000FF00),
                                 BitField('brake', 0x000000FF)),
                         bit_width=8 * 4),
                Register('AUDIO_CONTROL',
                         0x11,
                         fields=(BitField('peak_detection_time_ms',
                                          0b00001100,
                                          adapter=LookupAdapter({
                                              10: 0,
                                              20: 1,
                                              30: 2,
                                              40: 3
                                          })),
                                 BitField('low_pass_filter_hz',
                                          0b00000011,
                                          adapter=LookupAdapter({
                                              100: 0,
                                              125: 1,
                                              150: 2,
                                              200: 3
                                          })))),
                Register(
                    'AUDIO_INPUT_LEVEL',
                    0x12,
                    fields=(
                        BitField(
                            'minimum', 0xFF00
                        ),  # input level v = (minimum * 1.8) / 255 TODO create an adapter
                        BitField(
                            'maximum', 0x00FF
                        )  # input level v = (maximum * 1.8) / 255 TODO create an adapter
                    ),
                    bit_width=8 * 2),
                Register(
                    'AUDIO_OUTPUT_DRIVE',
                    0x14,
                    fields=(
                        BitField(
                            'minimum', 0xFF00
                        ),  # max drive % = (maximum / 255) x 100 TODO create an adapter
                        BitField(
                            'maximum', 0x00FF
                        )  # max drive % = (maximum / 255) x 100 TODO create an adapter
                    ),
                    bit_width=8 * 2),
                Register('VOLTAGE',
                         0x16,
                         fields=(BitField('rated', 0xFF00),
                                 BitField('overdrive_clamp', 0x00FF)),
                         bit_width=8 * 2),
                Register(
                    'AUTO_CALIBRATION_RESULT',
                    0x18,
                    fields=(
                        BitField('compensation',
                                 0xFF00),  # coef = 1 + compensation / 255
                        BitField(
                            'back_emf', 0x00FF
                        ),  # back-emf v = back_emf / 255 * 1.22 / back_emf_gain
                    ),
                    bit_width=8 * 2),
                Register(
                    'FEEDBACK_CONTROL',
                    0x1A,
                    fields=(
                        BitField('mode',
                                 0b10000000,
                                 adapter=LookupAdapter({
                                     'ERM': 0,
                                     'LRA': 1
                                 })),
                        BitField('feedback_brake_factor',
                                 0b01110000,
                                 adapter=LookupAdapter({
                                     1: 0,
                                     2: 1,
                                     3: 2,
                                     4: 3,
                                     6: 4,
                                     8: 5,
                                     16: 6,
                                     0: 7
                                 })),
                        BitField('loop_gain',
                                 0b00001100,
                                 adapter=LookupAdapter({
                                     'low': 0,
                                     'medium': 1,
                                     'high': 2,
                                     'very high': 3
                                 })),
                        BitField(
                            'back_emf_gain',
                            0b00000011,
                            adapter=LookupAdapter({
                                0.255: 0,  # ERM mode
                                0.7875: 1,
                                1.365: 2,
                                3.0: 3,
                                3.75: 0,  # LRA mode
                                7.5: 1,
                                15.0: 2,
                                22.5: 3
                            })))),
                Register('CONTROL1',
                         0x1B,
                         fields=(BitField('startup_boost', 0b10000000),
                                 BitField('ac_couple', 0b00100000),
                                 BitField('drive_time', 0b00011111))),
                Register('CONTROL2',
                         0x1C,
                         fields=(BitField('bidir_input', 0b10000000),
                                 BitField('brake_stabalize', 0b01000000),
                                 BitField('sample_time', 0b00110000),
                                 BitField('blanking_time', 0b00001100),
                                 BitField('idiss_time', 0b00000011))),
                Register('CONTROL3',
                         0x1D,
                         fields=(BitField('noise_gate_threshold',
                                          0b11000000,
                                          adapter=LookupAdapter({
                                              'Disabled': 0,
                                              '2%': 1,
                                              '4%': 2,
                                              '8%': 3
                                          })),
                                 BitField('erm_open_loop',
                                          0b00100000,
                                          adapter=LookupAdapter({
                                              'Closed Loop': 0,
                                              'Open Loop': 1
                                          })),
                                 BitField('supply_compensation_disable',
                                          0b00010000,
                                          adapter=LookupAdapter({
                                              'Enabled': 0,
                                              'Disabled': 1
                                          })),
                                 BitField('data_format_rtp',
                                          0b00001000,
                                          adapter=LookupAdapter({
                                              'Signed': 0,
                                              'Unsigned': 1
                                          })),
                                 BitField('lra_drive_mode',
                                          0b00000100,
                                          adapter=LookupAdapter({
                                              'Once': 0,
                                              'Twice': 1
                                          })),
                                 BitField('pwm_input_mode',
                                          0b00000010,
                                          adapter=LookupAdapter({
                                              'PWM': 0,
                                              'Analog': 1
                                          })),
                                 BitField('lra_open_loop',
                                          0b00000001,
                                          adapter=LookupAdapter({
                                              'Auto-resonance':
                                              0,
                                              'Open Loop':
                                              1
                                          })))),
                Register(
                    'CONTROL4',
                    0x1E,
                    fields=(
                        BitField('zero_crossing_detection_time',
                                 0b11000000,
                                 adapter=LookupAdapter({
                                     100: 0,
                                     200: 1,
                                     300: 2,
                                     390: 3
                                 })),
                        BitField(
                            'auto_calibration_time',
                            0b00110000,
                            adapter=LookupAdapter({
                                150: 0,  # 150ms to 350ms
                                250: 1,  # 250ms to 450ms
                                500: 2,  # 500ms to 700ms
                                1000: 3  # 1000ms to 1200ms
                            })),
                        # BitField('otp_status', 0b00000100),
                        # BitField('otp_program', 0b0000001)
                    )),
                Register('CONTROL5',
                         0x1F,
                         fields=(BitField('auto_open_loop_attempts',
                                          0b11000000,
                                          adapter=LookupAdapter({
                                              3: 0,
                                              4: 1,
                                              5: 2,
                                              6: 3
                                          })),
                                 BitField('auto_open_loop_transition',
                                          0b00100000),
                                 BitField('playback_interval_ms',
                                          0b00010000,
                                          adapter=LookupAdapter({
                                              5: 0,
                                              1: 1
                                          })),
                                 BitField('blanking_time', 0b00001100),
                                 BitField('idiss_time', 0b00000011))),
                Register(
                    'LRA_OPEN_LOOP_PERIOD',
                    0x20,
                    fields=(
                        BitField('period',
                                 0x7F),  # period (us) = period * 98.46
                    )),
                Register('VOLTAGE', 0x21, fields=(BitField('vbat', 0xFF), )),
                Register(
                    'LRA_RESONANCE',
                    0x22,
                    fields=(
                        BitField('period',
                                 0xFF),  # period (us) = period * 98.46
                    ))))

    def reset(self):
        self._drv2605.set('MODE', standby=False, reset=True)
        time.sleep(0.1)
        while self._drv2605.get('MODE').reset:
            time.sleep(0.01)
        self._drv2605.set('MODE', standby=False)

    def set_feedback_mode(self, mode='LRA'):
        self._drv2605.set('FEEDBACK_CONTROL', mode=mode)

    def set_library(self, library='LRA'):
        self._drv2605.set('LIBRARY_SELECTION', library='LRA')

    def set_mode(self, mode):
        self._drv2605.set('MODE', mode=mode)

    def auto_calibrate(self,
                       loop_gain='high',
                       feedback_brake_factor=2,
                       auto_calibration_time=1000,
                       zero_crossing_detection_time=100,
                       idiss_time=1):
        mode = self._drv2605.get('MODE').mode
        self._drv2605.set('MODE', mode='Auto Calibration')
        self._drv2605.set('FEEDBACK_CONTROL',
                          loop_gain=loop_gain,
                          feedback_brake_factor=feedback_brake_factor)
        self._drv2605.set(
            'CONTROL4',
            auto_calibration_time=auto_calibration_time,
            zero_crossing_detection_time=zero_crossing_detection_time)
        self._drv2605.set('CONTROL2', idiss_time=idiss_time)
        self._drv2605.set('GO', go=True)
        while self._drv2605.get('GO').go:
            time.sleep(0.01)
        self._drv2605.set('MODE', mode=mode)

    def set_realtime_input(self, value):
        """Set a single playback sample for realtime mode."""
        self._drv2605.set('REALTIME_PLAYBACK', input=value)

    def set_realtime_data_format(self, value):
        """Set the data format for realtime mode playback samples."""
        self._drv2605.set('CONTROL3', data_format_rtp=value)

    def set_sequence(self, *sequence):
        """Set a sequence to be played by the DRV2605.

        Accepts up to 8 arguments of type PlayWaveform or WaitMillis.

        """
        settings = {}
        for x, step in enumerate(sequence):
            if hasattr(step, 'wait_time'):
                settings['step{}_wait'.format(x + 1)] = step.wait_time
            elif hasattr(step, 'waveform'):
                settings['step{}_waveform'.format(x + 1)] = step.waveform
        self._drv2605.set('WAVEFORM_SEQUENCER', **settings)

    def go(self):
        """Trigger the current mode."""
        self._drv2605.set('GO', go=True)

    def stop(self):
        """Stop playback."""
        self._drv2605.set('GO', go=False)

    def busy(self):
        """Check if DRV2605 is busy."""
        return self._drv2605.get('GO').go
Beispiel #11
0
class DRV8830:
    def __init__(self, i2c_addr=I2C_ADDR1, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._drv8830 = Device(
            [I2C_ADDR1, I2C_ADDR2, I2C_ADDR3, I2C_ADDR4],
            i2c_dev=self._i2c_dev,
            registers=(
                Register(
                    'CONTROL',
                    0x00,
                    fields=(
                        BitField('voltage',
                                 0b11111100,
                                 adapter=VoltageAdapter()),  # vset
                        BitField('out2', 0b00000010),  # in2
                        BitField('out1', 0b00000001),  # in1
                        BitField(
                            'direction',
                            0b00000011,
                            adapter=LookupAdapter({  # both in2 and in1 :D
                                'coast': 0b00,
                                'reverse': 0b01,
                                'forward': 0b10,
                                'brake': 0b11
                            })))),
                Register(
                    'FAULT',
                    0x01,
                    fields=(
                        BitField(
                            'clear', 0b10000000
                        ),  # Clears fault status bits when written to 1
                        BitField('current_limit', 0b00010000
                                 ),  # Fault caused by external current limit
                        BitField(
                            'over_temperature', 0b00001000
                        ),  # Fault caused by over-temperature condition
                        BitField('under_voltage', 0b00000100
                                 ),  # Fault caused by undervoltage lockout
                        BitField(
                            'over_current',
                            0b00000010),  # Fault caused by overcurrent event
                        BitField('fault', 0b00000001)  # Fault condition exists
                    ))))

        self._drv8830.select_address(self._i2c_addr)

    def select_i2c_address(self, i2c_addr):
        self._i2c_addr = i2c_addr
        self._drv8830.select_address(self._i2c_addr)

    def set_outputs(self, out1, out2):
        """Set the individual driver outputs.
        
        Possible values are 1 (on) and 0 (off) with the following valid permutations:

        * 1 1 - brake
        * 0 0 - coast
        * 1 0 - forward
        * 0 1 - reverse
    
        """
        self._drv8830.set('CONTROL', out1=out1, out2=out2)

    def brake(self):
        """Set the driver outputs to braking mode."""
        self.set_direction('brake')

    def coast(self):
        """Set the driver outputs to coasting mode."""
        self.set_direction('coast')

    def forward(self):
        """Set the driver outputs to forward."""
        self.set_direction('forward')

    def reverse(self):
        """Set the driver outputs to reverse."""
        self.set_direction('reverse')

    def set_direction(self, direction):
        """Set the motor driver direction.

        Basically does the same thing as set_outputs, but takes
        a string name for direction, one of: coast, reverse,
        forward or brake.

        :param direction: string name of direction: coast, reverse, forward or brake

        """
        self._drv8830.set('CONTROL', direction=direction)

    def set_voltage(self, voltage):
        """Set the motor driver voltage.

        Roughly corresponds to motor speed depending upon the characteristics of your motor.

        :param voltage: from 0.48v to 5.06v

        """
        self._drv8830.set('CONTROL', voltage=voltage)

    def get_voltage(self):
        """Return the currently set motor voltage."""
        return self._drv8830.get('CONTROL').voltage

    def get_fault(self):
        """Get motor driver fault information.

        Returns a namedtuple of the fault flags:

        current_limit - external current limit exceeded (ilimit resistor), must clear fault or disable motor to reactivate
        over_temperature - driver has overheated, device resumes once temperature has dropped
        under_voltage - driver is below operating voltage (brownout), resumes once voltage has stabalised
        over_current - over-current protection activated, device disabled, must clear fault to reactivate
        fault - one or more fault flags is set

        """
        return self._drv8830.get('FAULT')

    def clear_fault(self):
        self._drv8830.set('FAULT', clear=True)
Beispiel #12
0
class MAX30105:
    def __init__(self, i2c_addr=I2C_ADDRESS, i2c_dev=None):
        self._is_setup = False
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._active_leds = 0
        self._max30105 = Device(
            I2C_ADDRESS,
            i2c_dev=self._i2c_dev,
            bit_width=8,
            registers=(
                Register('INT_STATUS_1',
                         0x00,
                         fields=(BitField('a_full', bit(7)),
                                 BitField('data_ready', bit(6)),
                                 BitField('alc_overflow', bit(5)),
                                 BitField('prox_int', bit(4)),
                                 BitField('pwr_ready', bit(0)))),
                Register('INT_STATUS_2',
                         0x01,
                         fields=(BitField('die_temp_ready', bit(1)), )),
                Register('INT_ENABLE_1',
                         0x02,
                         fields=(
                             BitField('a_full_en', bit(7)),
                             BitField('data_ready_en', bit(6)),
                             BitField('alc_overflow_en', bit(5)),
                             BitField('prox_int_en', bit(4)),
                         )),
                Register('INT_ENABLE_2',
                         0x03,
                         fields=(BitField('die_temp_ready_en', bit(1)), )),
                # Points to MAX30105 write location in FIFO
                Register('FIFO_WRITE',
                         0x04,
                         fields=(BitField('pointer', 0b00011111), )),
                # Counts the number of samples lost up to 0xf
                Register('FIFO_OVERFLOW',
                         0x05,
                         fields=(BitField('counter', 0b00011111), )),
                # Points to read location in FIFO
                Register('FIFO_READ',
                         0x06,
                         fields=(BitField('pointer', 0b00011111), )),
                Register('FIFO_CONFIG',
                         0x08,
                         fields=(BitField('sample_average',
                                          0b11100000,
                                          adapter=LookupAdapter({
                                              1: 0b000,
                                              2: 0b001,
                                              4: 0b010,
                                              8: 0b011,
                                              16: 0b100,
                                              32: 0b101
                                          })),
                                 BitField('fifo_rollover_en', 0b00010000),
                                 BitField('fifo_almost_full', 0b00001111))),
                Register('MODE_CONFIG',
                         0x09,
                         fields=(BitField('shutdown', 0b10000000),
                                 BitField('reset', 0b01000000),
                                 BitField('mode',
                                          0b00000111,
                                          adapter=LookupAdapter({
                                              'none':
                                              0b00,
                                              'red_only':
                                              0b010,
                                              'red_ir':
                                              0b011,
                                              'green_red_ir':
                                              0b111
                                          })))),
                Register(
                    'SPO2_CONFIG',
                    0x0A,
                    fields=(
                        BitField('adc_range_nA',
                                 0b01100000,
                                 adapter=LookupAdapter({
                                     2048: 0b00,
                                     4096: 0b01,
                                     8192: 0b10,
                                     16384: 0b11
                                 })),
                        BitField('sample_rate_sps',
                                 0b00011100,
                                 adapter=LookupAdapter({
                                     50: 0b000,
                                     100: 0b001,
                                     200: 0b010,
                                     400: 0b011,
                                     800: 0b100,
                                     1000: 0b101,
                                     1600: 0b110,
                                     3200: 0b111
                                 })),
                        BitField(
                            'led_pw_us',
                            0b00000011,
                            adapter=LookupAdapter({
                                69: 0b00,  # 68.95us
                                118: 0b01,  # 117.78us
                                215: 0b10,  # 215.44us
                                411: 0b11  # 410.75us
                            })))),
                Register('LED_PULSE_AMPLITUDE',
                         0x0C,
                         fields=(BitField('led1_mA',
                                          0xff0000,
                                          adapter=PulseAmplitudeAdapter()),
                                 BitField('led2_mA',
                                          0x00ff00,
                                          adapter=PulseAmplitudeAdapter()),
                                 BitField('led3_mA',
                                          0x0000ff,
                                          adapter=PulseAmplitudeAdapter())),
                         bit_width=24),
                Register('LED_PROX_PULSE_AMPLITUDE',
                         0x10,
                         fields=(BitField('pilot_mA',
                                          0xff,
                                          adapter=PulseAmplitudeAdapter()), )),
                # The below represent 4 timeslots
                Register('LED_MODE_CONTROL',
                         0x11,
                         fields=(BitField('slot2',
                                          0x7000,
                                          adapter=LEDModeAdapter()),
                                 BitField('slot1',
                                          0x0700,
                                          adapter=LEDModeAdapter()),
                                 BitField('slot4',
                                          0x0070,
                                          adapter=LEDModeAdapter()),
                                 BitField('slot3',
                                          0x0007,
                                          adapter=LEDModeAdapter())),
                         bit_width=16),
                Register('DIE_TEMP',
                         0x1f,
                         fields=(BitField('temperature',
                                          0xffff,
                                          adapter=TemperatureAdapter()), ),
                         bit_width=16),
                Register('DIE_TEMP_CONFIG',
                         0x21,
                         fields=(BitField('temp_en', bit(0)), )),
                Register('PROX_INT_THRESHOLD',
                         0x30,
                         fields=(BitField('threshold', 0xff), )),
                Register('PART_ID',
                         0xfe,
                         fields=(BitField('revision',
                                          0xff00), BitField('part', 0x00ff)),
                         bit_width=16)))

    def setup(self,
              led_power=6.4,
              sample_average=4,
              leds_enable=3,
              sample_rate=400,
              pulse_width=215,
              adc_range=16384,
              timeout=5.0):
        """Set up the sensor."""
        if self._is_setup:
            return
        self._is_setup = True

        self._active_leds = leds_enable

        self._max30105.select_address(self._i2c_addr)

        self.soft_reset(timeout=timeout)

        self._max30105.set('FIFO_CONFIG',
                           sample_average=sample_average,
                           fifo_rollover_en=True)

        self._max30105.set('SPO2_CONFIG',
                           sample_rate_sps=sample_rate,
                           adc_range_nA=adc_range,
                           led_pw_us=pulse_width)

        self._max30105.set('LED_PULSE_AMPLITUDE',
                           led1_mA=led_power,
                           led2_mA=led_power,
                           led3_mA=led_power)

        self._max30105.set('LED_PROX_PULSE_AMPLITUDE', pilot_mA=led_power)

        # Set the LED mode based on the number of LEDs we want enabled
        self._max30105.set('MODE_CONFIG',
                           mode=['red_only', 'red_ir',
                                 'green_red_ir'][leds_enable - 1])

        # Set up the LEDs requested in sequential slots
        self._max30105.set('LED_MODE_CONTROL',
                           slot1='red',
                           slot2='ir' if leds_enable >= 2 else 'off',
                           slot3='green' if leds_enable >= 3 else 'off')

        self.clear_fifo()

    def soft_reset(self, timeout=5.0):
        """Reset device."""
        self._max30105.set('MODE_CONFIG', reset=True)
        t_start = time.time()
        while self._max30105.get(
                'MODE_CONFIG').reset and time.time() - t_start < timeout:
            time.sleep(0.001)
        if self._max30105.get('MODE_CONFIG').reset:
            raise RuntimeError("Timeout: Failed to soft reset MAX30105.")

    def clear_fifo(self):
        """Clear samples FIFO."""
        self._max30105.set('FIFO_READ', pointer=0)
        self._max30105.set('FIFO_WRITE', pointer=0)
        self._max30105.set('FIFO_OVERFLOW', counter=0)

    def get_samples(self):
        """Return contents of sample FIFO."""
        ptr_r = self._max30105.get('FIFO_READ').pointer
        ptr_w = self._max30105.get('FIFO_WRITE').pointer

        if ptr_r == ptr_w:
            return None

        sample_count = ptr_w - ptr_r
        if sample_count < 0:
            sample_count = 32

        byte_count = sample_count * 3 * self._active_leds

        data = []

        while byte_count > 0:
            data += self._max30105._i2c.read_i2c_block_data(
                self._i2c_addr, 0x07, min(byte_count, 32))
            byte_count -= 32

        self.clear_fifo()

        result = []
        for x in range(0, len(data), 3):
            result.append((data[x] << 16) | (data[x + 1] << 8) | data[x + 2])

        return result

    def get_chip_id(self):
        """Return the revision and part IDs."""
        self.setup()

        part_id = self._max30105.get('PART_ID')

        return part_id.revision, part_id.part

    def get_temperature(self, timeout=5.0):
        """Return the die temperature."""
        self.setup()

        self._max30105.set('INT_ENABLE_2', die_temp_ready_en=True)
        self._max30105.set('DIE_TEMP_CONFIG', temp_en=True)
        t_start = time.time()

        while not self._max30105.get('INT_STATUS_2').die_temp_ready:
            time.sleep(0.01)
            if time.time() - t_start > timeout:
                raise RuntimeError(
                    'Timeout: Waiting for INT_STATUS_2, die_temp_ready.')

        return self._max30105.get('DIE_TEMP').temperature

    def set_mode(self, mode):
        """Set the sensor mode.

        :param mode: Mode, either red_only, red_ir or green_red_ir

        """
        self._max30105.set('MODE_CONFIG', mode=mode)

    def set_slot_mode(self, slot, mode):
        """Set the mode of a single slot.

        :param slot: Slot to set, either 1, 2, 3 or 4
        :param mode: Mode, either off, red, ir, green, pilot_red, pilot_ir or pilot_green

        """
        if slot == 1:
            self._max30105.set('LED_MODE_CONTROL', slot1=mode)
        elif slot == 2:
            self._max30105.set('LED_MODE_CONTROL', slot2=mode)
        elif slot == 3:
            self._max30105.set('LED_MODE_CONTROL', slot3=mode)
        elif slot == 4:
            self._max30105.set('LED_MODE_CONTROL', slot4=mode)
        else:
            raise ValueError("Invalid LED slot: {}".format(slot))

    def set_led_pulse_amplitude(self, led, amplitude):
        """Set the LED pulse amplitude in milliamps.

        :param led: LED to set, either 1, 2 or 3
        :param amplitude: LED amplitude in milliamps

        """
        if led == 1:
            self._max30105.set('LED_PULSE_AMPLITUDE', led1_mA=amplitude)
        elif led == 2:
            self._max30105.set('LED_PULSE_AMPLITUDE', led2_mA=amplitude)
        elif led == 3:
            self._max30105.set('LED_PULSE_AMPLITUDE', led3_mA=amplitude)
        else:
            raise ValueError("Invalid LED: {}".format(led))

    def set_fifo_almost_full_count(self, count):
        """Set number of FIFO slots remaining for Almost Full trigger.

        :param count: Count of remaining samples, from 0 to 15

        """
        self._max30105.set('FIFO_CONFIG', fifo_almost_full=count)

    def set_fifo_almost_full_enable(self, value):
        """Enable the FIFO-almost-full flag."""
        self._max30105.set('INT_ENABLE_1', a_full_en=value)

    def set_data_ready_enable(self, value):
        """Enable the data-ready flag."""
        self._max30105.set('INT_ENABLE_1', data_ready_en=value)

    def set_ambient_light_compensation_overflow_enable(self, value):
        """Enable the ambient light compensation overflow flag."""
        self._max30105.set('INT_ENABLE_1', alc_overflow_en=value)

    def set_proximity_enable(self, value):
        """Enable the proximity interrupt flag."""
        self._max30105.set('INT_ENABLE_1', prox_int_en=value)

    def set_proximity_threshold(self, value):
        """Set the threshold of the proximity sensor.

        Sets the infra-red ADC count that will trigger the start of particle-sensing mode.

        :param value: threshold value from 0 to 255

        """
        self._max30105.set('PROX_INT_THRESHOLD', threshold=value)

    def get_fifo_almost_full_status(self):
        """Get the FIFO-almost-full flag.

        This interrupt is set when the FIFO write pointer has N free spaces remaining, as defined in `set_fifo_almost_full_count`.

        The flag is cleared upon read.

        """
        return self._max30105.get('INT_STATUS_1').a_full

    def get_data_ready_status(self):
        """Get the data-ready flag.

        In particle-sensing mode this interrupt triggeres when a new sample has been placed into the FIFO.

        This flag is cleared upon read, or upon `get_samples()`

        """
        return self._max30105.get('INT_STATUS_1').data_ready

    def get_ambient_light_compensation_overflow_status(self):
        """Get the ambient light compensation overflow status flag.

        Returns True if the ALC has reached its limit, and ambient light is affecting the output of the ADC.

        This flag is cleared upon read.

        """
        return self._max30105.get('INT_STATUS_1').alc_overflow

    def get_proximity_triggered_threshold_status(self):
        """Get the proximity triggered threshold status flag.

        Returns True if the proximity threshold has been reached and particle-sensing mode has begun.

        This flag is cleared upon read.

        """
        return self._max30105.get('INT_STATUS_1').prox_int

    def get_power_ready_status(self):
        """Get the power ready status flag.

        Returns True if the sensor has successfully powered up and is ready to collect data.

        """
        return self._max30105.get('INT_STATUS_1').pwr_ready

    def get_die_temp_ready_status(self):
        """Get the die temperature ready flag.

        Returns True if the die temperature value is ready to be read.

        This flag is cleared upon read, or upon `get_temperature`.

        """
        return self._max30105.get('INT_STATUS_2').die_temp_ready
Beispiel #13
0
class ADS1015:
    def __init__(self,
                 i2c_addr=I2C_ADDRESS_DEFAULT,
                 alert_pin=None,
                 i2c_dev=None):
        self._lock = Lock()
        self._is_setup = False
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._alert_pin = alert_pin
        self._deprecated_channels = {
            'in0/ref': 'in0/in3',
            'in1/ref': 'in1/in3',
            'in2/ref': 'in2/in3',
            'ref/gnd': 'in3/gnd'
        }
        self._is_ads1115 = False

        self._ads1115 = Device(
            I2C_ADDRESSES,
            i2c_dev=self._i2c_dev,
            bit_width=8,
            registers=(
                Register('CONFIG',
                         0x01,
                         fields=(BitField('data_rate_sps',
                                          0b0000000011100000,
                                          adapter=LookupAdapter({
                                              8: 0b000,
                                              16: 0b001,
                                              32: 0b010,
                                              64: 0b011,
                                              128: 0b100,
                                              475: 0b101,
                                              860: 0b110
                                          })), )),
                Register('CONV',
                         0x00,
                         fields=(BitField('value',
                                          0xFFFF,
                                          adapter=Conv16Adapter()), ),
                         bit_width=16),
            ))
        self._ads1115.select_address(self._i2c_addr)

        self._ads1015 = Device(
            I2C_ADDRESSES,
            i2c_dev=self._i2c_dev,
            bit_width=8,
            registers=(
                Register(
                    'CONFIG',
                    0x01,
                    fields=(
                        BitField('operational_status',
                                 0b1000000000000000,
                                 adapter=LookupAdapter({
                                     'active': 0,
                                     'inactive_start': 1
                                 })),
                        BitField(
                            'multiplexer',
                            0b0111000000000000,
                            adapter=LookupAdapter({
                                'in0/in1':
                                0b000,  # Differential reading between in0 and in1, voltages must not be negative and must not exceed supply voltage
                                'in0/in3':
                                0b001,  # Differential reading between in0 and in3. pimoroni breakout onboard reference connected to in3
                                'in1/in3':
                                0b010,  # Differential reading between in1 and in3. pimoroni breakout onboard reference connected to in3
                                'in2/in3':
                                0b011,  # Differential reading between in2 and in3. pimoroni breakout onboard reference connected to in3
                                'in0/gnd':
                                0b100,  # Single-ended reading between in0 and GND
                                'in1/gnd':
                                0b101,  # Single-ended reading between in1 and GND
                                'in2/gnd':
                                0b110,  # Single-ended reading between in2 and GND
                                'in3/gnd':
                                0b111  # Single-ended reading between in3 and GND. Should always read 1.25v (or reference voltage) on pimoroni breakout
                            })),
                        BitField('programmable_gain',
                                 0b0000111000000000,
                                 adapter=LookupAdapter({
                                     6.144: 0b000,
                                     4.096: 0b001,
                                     2.048: 0b010,
                                     1.024: 0b011,
                                     0.512: 0b100,
                                     0.256: 0b101
                                 })),
                        BitField('mode',
                                 0b0000000100000000,
                                 adapter=LookupAdapter({
                                     'continuous': 0,
                                     'single': 1
                                 })),
                        BitField('data_rate_sps',
                                 0b0000000011100000,
                                 adapter=LookupAdapter({
                                     128: 0b000,
                                     250: 0b001,
                                     490: 0b010,
                                     920: 0b011,
                                     1600: 0b100,
                                     2400: 0b101,
                                     3300: 0b110
                                 })),
                        BitField(
                            'comparator_mode',
                            0b0000000000010000,
                            adapter=LookupAdapter({
                                'traditional':
                                0b0,  # Traditional comparator with hystersis
                                'window': 0b01
                            })),
                        BitField('comparator_polarity',
                                 0b0000000000001000,
                                 adapter=LookupAdapter({
                                     'active_low': 0b0,
                                     'active_high': 0b1
                                 })),
                        BitField('comparator_latching', 0b0000000000000100),
                        BitField('comparator_queue',
                                 0b0000000000000011,
                                 adapter=LookupAdapter({
                                     'one': 0b00,
                                     'two': 0b01,
                                     'four': 0b10,
                                     'disabled': 0b11
                                 }))),
                    bit_width=16),
                Register('CONV',
                         0x00,
                         fields=(BitField('value',
                                          0xFFF0,
                                          adapter=ConvAdapter()), ),
                         bit_width=16),
                Register('THRESHOLD',
                         0x02,
                         fields=(BitField('low', 0xFFFF, adapter=S16Adapter()),
                                 BitField('high', 0xFFFF,
                                          adapter=S16Adapter())),
                         bit_width=32)))
        self._ads1015.select_address(self._i2c_addr)

    @synchronized
    def detect_chip_type(self, timeout=10.0):
        """Attempt to auto-detect if an ADS1015 or ADS1115 is connected."""
        # 250sps is 16sps on ADS1115
        sps = 128
        samples = 10
        self._ads1015.set('CONFIG', mode='single')
        self._ads1115.set('CONFIG', data_rate_sps=sps)
        runtimes = []
        for x in range(samples):
            self.start_conversion()
            t_start = time.time()

            while self._ads1015.get('CONFIG').operational_status == 'active':
                if (time.time() - t_start) > timeout:
                    raise ADS1015TimeoutError(
                        "Timed out waiting for conversion.")
                time.sleep(0.001)

            runtimes.append(time.time() - t_start)
            time.sleep(0.001)

        runtime = sum(runtimes) / float(samples)
        # If it takes roughly the same time or longer per sample,
        # then it's an ADS1115. An ADS1015 would be roughly 16x faster.
        self._is_ads1115 = runtime >= (1.0 / sps)
        return [DEVICE_ADS1015, DEVICE_ADS1115][self._is_ads1115]

    def start_conversion(self):
        """Start a conversion."""
        self.set_status('inactive_start')

    def conversion_ready(self):
        """Check if conversion is ready."""
        return self.get_status() != 'active'

    def set_status(self, value):
        """Set the operational status.

        :param value: Set to true to trigger a conversion, false will have no effect.

        """
        self._ads1015.set('CONFIG', operational_status=value)

    def get_status(self):
        """Get the operational status.

        Result will be true if the ADC is actively performing a conversion and false if it has completed.

        """
        return self._ads1015.get('CONFIG').operational_status

    def set_multiplexer(self, value):
        """Set the analog multiplexer.

        Sets up the analog input in single or differential modes.

        If b is specified as gnd the ADC will be in single-ended mode, referenced against Ground.

        If b is specified as in1 or ref the ADC will be in differential mode.

        a should be one of in0, in1, in2, or in3

        This method has no function on the ADS1013 or ADS1014

        'in0/in1' - Differential reading between in0 and in1, voltages must not be negative and must not exceed supply voltage
        'in0/in3' - Differential reading between in0 and in3
        'in1/in3' - Differential reading between in1 and in3
        'in2/in3' - Differential reading between in2 and in3
        'in0/gnd' - Single-ended reading between in0 and GND
        'in1/gnd' - Single-ended reading between in1 and GND
        'in2/gnd' - Single-ended reading between in2 and GND
        'in3/gnd' - Should always read 1.25v (or reference voltage)

        :param value: Takes the form a/b

        """
        if value in self._deprecated_channels.keys():
            value = self._deprecated_channels[value]
        self._ads1015.set('CONFIG', multiplexer=value)

    def get_multiplexer(self):
        """Return the current analog multiplexer state."""
        return self._ads1015.get('CONFIG').multiplexer

    def set_mode(self, value):
        """Set the analog mode.

        In single-mode you must trigger a conversion manually by writing the status bit.

        :param value: One of 'continuous' or 'single'

        """
        self._ads1015.set('CONFIG', mode=value)

    def get_mode(self):
        """Get the analog mode."""
        return self._ads1015.get('CONFIG').mode

    def set_programmable_gain(self, value=2.048):
        """Set the analog gain. This has no function on the ADS1013.

        Sets up the full-scale range and resolution of the ADC in volts.

        The range is always differential, so a value of 6.144v would give a range of +-6.144.

        A single-ended reading will therefore always have only 11-bits of resolution, since the 12th bit is the (unused) sign bit.

        :param value: the range in volts - one of 6.144, 4.096, 2.048 (default), 1.024, 0.512 or 0.256

        """
        self._ads1015.set('CONFIG', programmable_gain=value)

    def get_programmable_gain(self):
        """Return the curren gain setting."""
        return self._ads1015.get('CONFIG').programmable_gain

    def set_sample_rate(self, value=1600):
        """Set the analog sample rate.

        :param value: The sample rate in samples-per-second

        Valid values for ADS1015 are 128, 250, 490, 920, 1600 (default), 2400 or 3300
        Valid values for ADS1115 are 8, 16, 32, 64, 128 (default), 250, 475, 860

        """
        if self._is_ads1115:
            self._ads1115.set('CONFIG', data_rate_sps=value)
        else:
            self._ads1015.set('CONFIG', data_rate_sps=value)

    def get_sample_rate(self):
        """Return the current sample-rate setting."""
        self._ads1015.get('CONFIG').data_rate_sps

    def set_comparator_mode(self, value):
        """Set the analog comparator mode.

        In traditional mode the comparator asserts the alert/ready pin when the conversion data exceeds the high threshold and de-asserts when it falls below the low threshold.

        In window mode the comparator asserts the alert/ready pin when the conversion data exceeds the high threshold or falls below the low threshold.

        :param value: Either 'traditional' or 'window'

        """
        self._ads1015.set('CONFIG', comparator_mode=value)

    def get_comparator_mode(self):
        """Return the current comparator mode."""
        self._ads1015.get('CONFIG').comparator_mode

    def set_comparator_latching(self, value):
        self._ads1015.set('CONFIG', comparator_latching=value)

    def get_comparator_latching(self):
        self._ads1015.get('CONFIG').comparator_latching

    def set_comparator_queue(self, value):
        self._ads1015.set('CONFIG', comparator_queue=value)

    def get_comparator_queue(self):
        return self._ads1015.get('CONFIG').comparator_queue

    def wait_for_conversion(self, timeout=10):
        """Wait for ADC conversion to finish.

        Timeout exception is alised as ads1015.ADS1015TimeoutError for convinience.

        :param timeout: conversion timeout in seconds

        :raises TimeoutError in Python 3.x
        :raises socket.timeout in Python 2.x

        """
        t_start = time.time()
        while not self.conversion_ready():
            time.sleep(0.001)
            if (time.time() - t_start) > timeout:
                raise ADS1015TimeoutError("Timed out waiting for conversion.")

    def get_reference_voltage(self):
        """Read the reference voltage that is included on the pimoroni PM422 breakout."""
        return self.get_voltage(channel='in3/gnd')

    @synchronized
    def get_voltage(self, channel=None):
        """Read the raw voltage of a channel."""
        if channel is not None:
            self.set_multiplexer(channel)

        self.start_conversion()
        self.wait_for_conversion()

        value = self.get_conversion_value()
        gain = self.get_programmable_gain()
        gain *= 1000.0  # Convert gain from V to mV
        if self._is_ads1115:
            value /= 32768.0  # Divide by total register size
        else:
            value /= 2048.0
        value *= float(gain)  # Multiply by current gain value to get mV
        value /= 1000.0  # mV to V
        return value

    def get_compensated_voltage(self,
                                channel=None,
                                vdiv_a=8060000,
                                vdiv_b=402000,
                                reference_voltage=1.241):
        """Read and compensate the voltage of a channel."""
        pin_v = self.get_voltage(channel=channel)
        input_v = pin_v * (float(vdiv_a + vdiv_b) / float(vdiv_b))
        input_v += reference_voltage
        return round(input_v, 3)

    def get_conversion_value(self):
        if self._is_ads1115:
            return self._ads1115.get('CONV').value
        else:
            return self._ads1015.get('CONV').value

    def set_low_threshold(self, value):
        self._ads1015.set('THRESHOLD', low=value)

    def get_low_threshold(self):
        self._ads1015.get('THRESHOLD').low

    def set_high_threshold(self, value):
        self._ads1015.set('THRESHOLD', high=value)

    def get_high_threshold(self):
        self._ads1015.get('THRESHOLD').high
Beispiel #14
0
class LTR559:
    def __init__(self, i2c_dev=None, enable_interrupts=False, interrupt_pin_polarity=1, timeout=5.0):
        """Initialise the LTR559.

        This sets up the LTR559 and checks that the Part Number ID matches 0x09 and
        that the Revision Number ID matches 0x02. If you come across an unsupported
        revision you should raise an Issue at https://github.com/pimoroni/ltr559-python

        Several known-good default values are picked during setup, and the interrupt
        thresholds are reset to the full range so that interrupts will not fire unless
        configured manually using `set_light_threshold` and `set_proximity_threshold`.

        Interrupts are always enabled, since this must be done before the sensor is active.

        """
        self._als0 = 0
        self._als1 = 0
        self._ps0 = 0
        self._lux = 0
        self._ratio = 100

        # Non default
        self._gain = 4  # 4x gain = 0.25 to 16k lux
        self._integration_time = 50

        self._ch0_c = (17743, 42785, 5926, 0)
        self._ch1_c = (-11059, 19548, -1185, 0)

        self._ltr559 = Device(I2C_ADDR, i2c_dev=i2c_dev, bit_width=8, registers=(
            Register('ALS_CONTROL', 0x80, fields=(
                BitField('gain', 0b00011100, adapter=LookupAdapter({
                    1: 0b000,
                    2: 0b001,
                    4: 0b010,
                    8: 0b011,
                    48: 0b110,
                    96: 0b111})),
                BitField('sw_reset', 0b00000010),
                BitField('mode', 0b00000001)
            )),

            Register('PS_CONTROL', 0x81, fields=(
                BitField('saturation_indicator_enable', 0b00100000),
                BitField('active', 0b00000011, adapter=LookupAdapter({
                    False: 0b00,
                    True: 0b11}))
            )),

            Register('PS_LED', 0x82, fields=(
                BitField('pulse_freq_khz', 0b11100000, adapter=LookupAdapter({
                    30: 0b000,
                    40: 0b001,
                    50: 0b010,
                    60: 0b011,
                    70: 0b100,
                    80: 0b101,
                    90: 0b110,
                    100: 0b111})),
                BitField('duty_cycle', 0b00011000, adapter=LookupAdapter({
                    0.25: 0b00,
                    0.5: 0b01,
                    0.75: 0b10,
                    1.0: 0b11})),
                BitField('current_ma', 0b00000111, adapter=LookupAdapter({
                    5: 0b000,
                    10: 0b001,
                    20: 0b010,
                    50: 0b011,
                    100: 0b100}))
            )),

            Register('PS_N_PULSES', 0x83, fields=(
                BitField('count', 0b00001111),
            )),

            Register('PS_MEAS_RATE', 0x84, fields=(
                BitField('rate_ms', 0b00001111, adapter=LookupAdapter({
                    10: 0b1000,
                    50: 0b0000,
                    70: 0b0001,
                    100: 0b0010,
                    200: 0b0011,
                    500: 0b0100,
                    1000: 0b0101,
                    2000: 0b0110})),
            )),

            Register('ALS_MEAS_RATE', 0x85, fields=(
                BitField('integration_time_ms', 0b00111000, adapter=LookupAdapter({
                    100: 0b000,
                    50: 0b001,
                    200: 0b010,
                    400: 0b011,
                    150: 0b100,
                    250: 0b101,
                    300: 0b110,
                    350: 0b111})),
                BitField('repeat_rate_ms', 0b00000111, adapter=LookupAdapter({
                    50: 0b000,
                    100: 0b001,
                    200: 0b010,
                    500: 0b011,
                    1000: 0b100,
                    2000: 0b101}))
            )),

            Register('PART_ID', 0x86, fields=(
                BitField('part_number', 0b11110000),  # Should be 0x09H
                BitField('revision', 0b00001111)      # Should be 0x02H
            ), read_only=True, volatile=False),

            Register('MANUFACTURER_ID', 0x87, fields=(
                BitField('manufacturer_id', 0b11111111),  # Should be 0x05H
            ), read_only=True),

            # This will address 0x88, 0x89, 0x8A and 0x8B as a continuous 32bit register
            Register('ALS_DATA', 0x88, fields=(
                BitField('ch1', 0xFFFF0000, bit_width=16, adapter=U16ByteSwapAdapter()),
                BitField('ch0', 0x0000FFFF, bit_width=16, adapter=U16ByteSwapAdapter())
            ), read_only=True, bit_width=32),

            Register('ALS_PS_STATUS', 0x8C, fields=(
                BitField('als_data_valid', 0b10000000),
                BitField('als_gain', 0b01110000, adapter=LookupAdapter({
                    1: 0b000,
                    2: 0b001,
                    4: 0b010,
                    8: 0b011,
                    48: 0b110,
                    96: 0b111})),
                BitField('als_interrupt', 0b00001000),  # True = Interrupt is active
                BitField('als_data', 0b00000100),       # True = New data available
                BitField('ps_interrupt', 0b00000010),   # True = Interrupt is active
                BitField('ps_data', 0b00000001)         # True = New data available
            ), read_only=True),

            # The PS data is actually an 11bit value but since B3 is reserved it'll (probably) read as 0
            # We could mask the result if necessary
            Register('PS_DATA', 0x8D, fields=(
                BitField('ch0', 0xFF0F, adapter=Bit12Adapter()),
                BitField('saturation', 0x0080)
            ), bit_width=16, read_only=True),

            # INTERRUPT allows the interrupt pin and function behaviour to be configured.
            Register('INTERRUPT', 0x8F, fields=(
                BitField('polarity', 0b00000100),
                BitField('mode', 0b00000011, adapter=LookupAdapter({
                    'off': 0b00,
                    'ps': 0b01,
                    'als': 0b10,
                    'als+ps': 0b11}))
            )),

            Register('PS_THRESHOLD', 0x90, fields=(
                BitField('upper', 0xFF0F0000, adapter=Bit12Adapter()),
                BitField('lower', 0x0000FF0F, adapter=Bit12Adapter())
            ), bit_width=32),

            # PS_OFFSET defines the measurement offset value to correct for proximity
            # offsets caused by device variations, crosstalk and other environmental factors.
            Register('PS_OFFSET', 0x94, fields=(
                BitField('offset', 0x03FF),  # Last two bits of 0x94, full 8 bits of 0x95
            ), bit_width=16),

            # Defines the upper and lower limits of the ALS reading.
            # An interrupt is triggered if values fall outside of this range.
            # See also INTERRUPT_PERSIST.
            Register('ALS_THRESHOLD', 0x97, fields=(
                BitField('upper', 0xFFFF0000, adapter=U16ByteSwapAdapter(), bit_width=16),
                BitField('lower', 0x0000FFFF, adapter=U16ByteSwapAdapter(), bit_width=16)
            ), bit_width=32),

            # This register controls how many values must fall outside of the range defined
            # by upper and lower threshold limits before the interrupt is asserted.

            # In the case of both PS and ALS, a 0 value indicates that every value outside
            # the threshold range should be counted.
            # Values therein map to n+1 , ie: 0b0001 requires two consecutive values.
            Register('INTERRUPT_PERSIST', 0x9E, fields=(
                BitField('PS', 0xF0),
                BitField('ALS', 0x0F)
            ))

        ))

        """Set up the LTR559 sensor"""
        self.part_id = self._ltr559.get('PART_ID')
        if self.part_id.part_number != PART_ID or self.part_id.revision != REVISION_ID:
            raise RuntimeError("LTR559 not found")

        self._ltr559.set('ALS_CONTROL', sw_reset=1)

        t_start = time.time()
        while time.time() - t_start < timeout:
            status = self._ltr559.get('ALS_CONTROL').sw_reset
            if status == 0:
                break
            time.sleep(0.05)

        if self._ltr559.get('ALS_CONTROL').sw_reset:
            raise RuntimeError("Timeout waiting for software reset.")

        # Interrupt register must be set before device is switched to active mode
        # see datasheet page 12/40, note #2.
        if enable_interrupts:
            self._ltr559.set('INTERRUPT',
                             mode='als+ps',
                             polarity=interrupt_pin_polarity)

        # FIXME use datasheet defaults or document
        # No need to run the proximity LED at 100mA, so we pick 50 instead.
        # Tests suggest this works pretty well.
        self._ltr559.set('PS_LED',
                         current_ma=50,
                         duty_cycle=1.0,
                         pulse_freq_khz=30)

        # 1 pulse is the default value
        self._ltr559.set('PS_N_PULSES', count=1)

        self._ltr559.set('ALS_CONTROL',
                         mode=1,
                         gain=self._gain)

        self._ltr559.set('PS_CONTROL',
                         active=True,
                         saturation_indicator_enable=1)

        self._ltr559.set('PS_MEAS_RATE', rate_ms=100)

        self._ltr559.set('ALS_MEAS_RATE',
                         integration_time_ms=self._integration_time,
                         repeat_rate_ms=50)

        self._ltr559.set('ALS_THRESHOLD',
                         lower=0x0000,
                         upper=0xFFFF)

        self._ltr559.set('PS_THRESHOLD',
                         lower=0x0000,
                         upper=0xFFFF)

        self._ltr559.set('PS_OFFSET', offset=0)

    def get_part_id(self):
        """Get part number.

        Returns the Part Number ID portion of the PART_ID register.

        This should always equal 0x09,
        differences may indicate a sensor incompatible with this library.

        """
        return self.part_id.part_number

    def get_revision(self):
        """Get revision ID.

        Returns the Revision ID portion of the PART_ID register.

        This library was tested against revision 0x02,
        differences may indicate a sensor incompatible with this library.

        """
        return self.part_id.revision

    def set_light_threshold(self, lower, upper):
        """Set light interrupt threshold.

        Set the upper and lower threshold for the light sensor interrupt.

        An interrupt is triggered for readings *outside* of this range.

        Since the light threshold is specified in raw counts and applies to
        both ch0 and ch1 it's not possible to interrupt at a specific Lux value.

        :param lower: Lower threshold in raw ADC counts
        :param upper: Upper threshold in raw ADC counts

        """
        self._ltr559.set('ALS_THRESHOLD',
                         lower=lower,
                         upper=upper)

    def set_proximity_threshold(self, lower, upper):
        """Set proximity interrupt threshold.

        Set the upper and lower threshold for the proximity interrupt.

        An interrupt is triggered for readings *outside* of this range.

        :param lower: Lower threshold
        :param upper: Upper threshold

        """
        self._ltr559.set('PS_THRESHOLD',
                         lower=lower,
                         upper=upper)

    def set_proximity_rate_ms(self, rate_ms):
        """Set proximity measurement repeat rate in milliseconds.

        This is the rate at which the proximity sensor is measured. For example:
        A rate of 100ms would result in ten proximity measurements every second.

        :param rate_ms: Time in milliseconds- one of 10, 50, 70, 100, 200, 500, 1000 or 2000

        """
        self._ltr559.set('PS_MEAS_RATE', rate_ms=rate_ms)

    def set_light_integration_time_ms(self, time_ms):
        """Set light integration time in milliseconds.

        This is the measurement time for each individual light sensor measurement,
        it must be equal to or less than the repeat rate.

        :param time_ms: Time in milliseconds- one of 50, 100, 150, 200, 300, 350, 400

        """
        self._integration_time = time_ms
        self._ltr559.set('ALS_MEAS_RATE', integration_time_ms=time_ms)

    def set_light_repeat_rate_ms(self, rate_ms=100):
        """Set light measurement repeat rate in milliseconds.

        This is the rate at which light measurements are repeated. For example:
        A repeat rate of 1000ms would result in one light measurement every second.

        The repeat rate must be equal to or larger than the integration time, if a lower
        value is picked then it is automatically reset by the LTR559 to match the chosen
        integration time.

        :param rate_ms: Rate in milliseconds- one of 50, 100, 200, 500, 1000 or 2000

        """
        self._ltr559.set('ALS_MEAS_RATE', repeat_rate_ms=rate_ms)

    def set_interrupt_mode(self, enable_light=True, enable_proximity=True):
        """Set the intterupt mode

        :param enable_light: Enable the light sensor interrupt
        :param enable_proximity: Enable the proximity sensor interrupt

        """
        mode = []

        if enable_light:
            mode.append('als')

        if enable_proximity:
            mode.append('ps')

        self._ltr559.set('INTERRUPT', mode='+'.join(mode))

    def set_proximity_active(self, active=True):
        """Enable/disable proximity sensor

        :param active: True for enabled, False for disabled

        """
        self._ltr559.set('PS_CONTROL', active=active)

    def set_proximity_saturation_indictator(self, enabled=True):
        """Enable/disable the proximity saturation indicator

        :param enabled: True for enabled, False for disabled

        """
        self._ltr559.set('PS_CONTROL', saturation_indicator_enable=enabled)

    def set_proximity_offset(self, offset):
        """Setup the proximity compensation offset

        :param offset: Offset value from 0 to 1023

        """
        return self._ltr559.set('PS_OFFSET', offset=offset)

    def set_proximity_led(self, current_ma=50, duty_cycle=1.0, pulse_freq_khz=30, num_pulses=1):
        """Setup the proximity led current and properties.

        :param current_ma: LED current in milliamps- one of 5, 10, 20, 50 or 100
        :param duty_cycle: LED duty cucle- one of 0.25, 0.5, 0.75 or 1.0 (25%, 50%, 75% or 100%)
        :param pulse_freq_khz: LED pulse frequency- one of 30, 40, 50, 60, 70, 80, 90 or 100
        :param num_pulse: Number of LED pulses to be emitted- 1 to 15

        """
        self._ltr559.set('PS_LED',
                         current_ma=current_ma,
                         duty_cycle=duty_cycle,
                         set_pulse_freq_khz=pulse_freq_khz)

        self._ltr559.set('PS_N_PULSES', num_pulses)

    def set_light_options(self, active=True, gain=4):
        """Set the mode and gain for the light sensor.

        By default the sensor is active with a gain of 4x (0.25 to 16k lux).

        :param active: True for Active Mode, False for Stand-by Mode
        :param gain: Light sensor gain x- one of 1, 2, 4, 8, 48 or 96

        1x = 1 to 64k lux
        2x = 0.5 to 32k lux
        4x = 0.25 to 16k lux
        8x = 0.125 to 8k lux
        48x = 0.02 to 1.3k lux
        96x = 0.01 to 600 lux
        """
        self._gain = gain
        self._ltr559.set('ALS_CONTROL',
                         mode=active,
                         gain=gain)

    def update_sensor(self):
        """Update the sensor lux and proximity values.

        Will perform a read of the status register and determine if either an interrupt
        has triggered or the new data flag for the light or proximity sensor is flipped.

        If new data is present it will be calculated and stored for later retrieval.

        Proximity data is stored in `self._ps0` and can be retrieved with `get_proximity`.

        Light sensor data is stored in `self._lux` and can be retrieved with `get_lux`.

        Raw light sensor data is also stored in `self._als0` and self._als1` which store
        the ch0 and ch1 values respectively. These can be retrieved with `get_raw_als`.

        """
        status = self._ltr559.get('ALS_PS_STATUS')
        ps_int = status.ps_interrupt or status.ps_data
        als_int = status.als_interrupt or status.als_data

        if ps_int:
            self._ps0 = self._ltr559.get('PS_DATA').ch0

        if als_int:
            als = self._ltr559.get('ALS_DATA')
            self._als0 = als.ch0
            self._als1 = als.ch1

            self._ratio = self._als1 * 100 / (self._als1 + self._als0) if self._als0 + self._als1 > 0 else 101

            if self._ratio < 45:
                ch_idx = 0
            elif self._ratio < 64:
                ch_idx = 1
            elif self._ratio < 85:
                ch_idx = 2
            else:
                ch_idx = 3

            try:
                self._lux = (self._als0 * self._ch0_c[ch_idx]) - (self._als1 * self._ch1_c[ch_idx])
                self._lux /= (self._integration_time / 100.0)
                self._lux /= self._gain
                self._lux /= 10000.0
            except ZeroDivisionError:
                self._lux = 0

    def get_gain(self):
        """Return gain used in lux calculation."""
        return self._gain

    def get_integration_time(self):
        """Return integration time used in lux calculation.

        Integration time directly affects the raw ch0 and ch1 readings and is required
        to correctly calculate a lux value.

        """
        return self._integration_time

    get_intt = get_integration_time

    def get_raw_als(self, passive=True):
        """Return raw ALS channel data ch0, ch1.

        The two channels measure Visible + IR and just IR respectively.

        """
        if not passive:
            self.update_sensor()
        return self._als0, self._als1

    def get_ratio(self, passive=True):
        """Return the ambient light ratio between ALS channels.

        Uses the IR-only channel to discount the portion of IR from the unfiltered channel.

        """
        if not passive:
            self.update_sensor()
        return self._ratio

    def get_lux(self, passive=False):
        """Return the ambient light value in lux."""
        if not passive:
            self.update_sensor()
        return self._lux

    def get_interrupt(self):
        """Return the light and proximity sensor interrupt status.

        An interrupt is asserted when the light or proximity sensor reading is *outside*
        the range set by the upper and lower thresholds.

        """
        interrupt = self._ltr559.get('ALS_PS_STATUS')
        return interrupt.als_interrupt, interrupt.ps_interrupt

    def get_proximity(self, passive=False):
        """Return the proximity.

        Returns the raw proximity reading from the sensor.

        A closer object produces a larger value.

        """
        if not passive:
            self.update_sensor()
        return self._ps0
Beispiel #15
0
class LTR559:
    def __init__(self, i2c_dev=None, enable_interrupts=False, interrupt_pin_polarity=1, timeout=5.0):
        self._als0 = 0
        self._als1 = 0
        self._ps0 = 0
        self._lux = 0
        self._gain = 4
        self._ratio = 100

        # Non default
        self._integration_time = 50

        self._ch0_c = (17743, 42785, 5926, 0)
        self._ch1_c = (-11059, 19548, -1185, 0)

        self._ltr559 = Device(I2C_ADDR, i2c_dev=i2c_dev, bit_width=8, registers=(
            Register('ALS_CONTROL', 0x80, fields=(
                BitField('gain', 0b00011100, adapter=LookupAdapter({
                    1: 0b000,
                    2: 0b001,
                    4: 0b010,
                    8: 0b011,
                    48: 0b110,
                    96: 0b111})),
                BitField('sw_reset', 0b00000010),
                BitField('mode', 0b00000001)
            )),

            Register('PS_CONTROL', 0x81, fields=(
                BitField('saturation_indicator_enable', 0b00100000),
                BitField('active', 0b00000011, adapter=LookupAdapter({
                    False: 0b00,
                    True: 0b11}))
            )),

            Register('PS_LED', 0x82, fields=(
                BitField('pulse_freq_khz', 0b11100000, adapter=LookupAdapter({
                    30: 0b000,
                    40: 0b001,
                    50: 0b010,
                    60: 0b011,
                    70: 0b100,
                    80: 0b101,
                    90: 0b110,
                    100: 0b111})),
                BitField('duty_cycle', 0b00011000, adapter=LookupAdapter({
                    0.25: 0b00,
                    0.5: 0b01,
                    0.75: 0b10,
                    1.0: 0b11})),
                BitField('current_ma', 0b00000111, adapter=LookupAdapter({
                    5: 0b000,
                    10: 0b001,
                    20: 0b010,
                    50: 0b011,
                    100: 0b100}))
            )),

            Register('PS_N_PULSES', 0x83, fields=(
                BitField('count', 0b00001111),
            )),

            Register('PS_MEAS_RATE', 0x84, fields=(
                BitField('rate_ms', 0b00001111, adapter=LookupAdapter({
                    10: 0b1000,
                    50: 0b0000,
                    70: 0b0001,
                    100: 0b0010,
                    200: 0b0011,
                    500: 0b0100,
                    1000: 0b0101,
                    2000: 0b0110})),
            )),

            Register('ALS_MEAS_RATE', 0x85, fields=(
                BitField('integration_time_ms', 0b00111000, adapter=LookupAdapter({
                    100: 0b000,
                    50: 0b001,
                    200: 0b010,
                    400: 0b011,
                    150: 0b100,
                    250: 0b101,
                    300: 0b110,
                    350: 0b111})),
                BitField('repeat_rate_ms', 0b00000111, adapter=LookupAdapter({
                    50: 0b000,
                    100: 0b001,
                    200: 0b010,
                    500: 0b011,
                    1000: 0b100,
                    2000: 0b101}))
            )),

            Register('PART_ID', 0x86, fields=(
                BitField('part_number', 0b11110000),  # Should be 0x09H
                BitField('revision', 0b00001111)      # Should be 0x02H
            ), read_only=True, volatile=False),

            Register('MANUFACTURER_ID', 0x87, fields=(
                BitField('manufacturer_id', 0b11111111),  # Should be 0x05H
            ), read_only=True),

            # This will address 0x88, 0x89, 0x8A and 0x8B as a continuous 32bit register
            Register('ALS_DATA', 0x88, fields=(
                BitField('ch1', 0xFFFF0000, bit_width=16, adapter=U16ByteSwapAdapter()),
                BitField('ch0', 0x0000FFFF, bit_width=16, adapter=U16ByteSwapAdapter())
            ), read_only=True, bit_width=32),

            Register('ALS_PS_STATUS', 0x8C, fields=(
                BitField('als_data_valid', 0b10000000),
                BitField('als_gain', 0b01110000, adapter=LookupAdapter({
                    1: 0b000,
                    2: 0b001,
                    4: 0b010,
                    8: 0b011,
                    48: 0b110,
                    96: 0b111})),
                BitField('als_interrupt', 0b00001000),  # True = Interrupt is active
                BitField('als_data', 0b00000100),       # True = New data available
                BitField('ps_interrupt', 0b00000010),   # True = Interrupt is active
                BitField('ps_data', 0b00000001)         # True = New data available
            ), read_only=True),

            # The PS data is actually an 11bit value but since B3 is reserved it'll (probably) read as 0
            # We could mask the result if necessary
            Register('PS_DATA', 0x8D, fields=(
                BitField('ch0', 0xFF0F, adapter=Bit12Adapter()),
                BitField('saturation', 0x0080)
            ), bit_width=16, read_only=True),

            # INTERRUPT allows the interrupt pin and function behaviour to be configured.
            Register('INTERRUPT', 0x8F, fields=(
                BitField('polarity', 0b00000100),
                BitField('mode', 0b00000011, adapter=LookupAdapter({
                    'off': 0b00,
                    'ps': 0b01,
                    'als': 0b10,
                    'als+ps': 0b11}))
            )),

            Register('PS_THRESHOLD', 0x90, fields=(
                BitField('upper', 0xFF0F0000, adapter=Bit12Adapter()),
                BitField('lower', 0x0000FF0F, adapter=Bit12Adapter())
            ), bit_width=32),

            # PS_OFFSET defines the measurement offset value to correct for proximity
            # offsets caused by device variations, crosstalk and other environmental factors.
            Register('PS_OFFSET', 0x94, fields=(
                BitField('offset', 0x03FF),  # Last two bits of 0x94, full 8 bits of 0x95
            ), bit_width=16),

            # Defines the upper and lower limits of the ALS reading.
            # An interrupt is triggered if values fall outside of this range.
            # See also INTERRUPT_PERSIST.
            Register('ALS_THRESHOLD', 0x97, fields=(
                BitField('upper', 0xFFFF0000, adapter=U16ByteSwapAdapter(), bit_width=16),
                BitField('lower', 0x0000FFFF, adapter=U16ByteSwapAdapter(), bit_width=16)
            ), bit_width=32),

            # This register controls how many values must fall outside of the range defined
            # by upper and lower threshold limits before the interrupt is asserted.

            # In the case of both PS and ALS, a 0 value indicates that every value outside
            # the threshold range should be counted.
            # Values therein map to n+1 , ie: 0b0001 requires two consecutive values.
            Register('INTERRUPT_PERSIST', 0x9E, fields=(
                BitField('PS', 0xF0),
                BitField('ALS', 0x0F)
            ))

        ))

        """Set up the LTR559 sensor"""
        self.part_id = self._ltr559.get('PART_ID')
        if self.part_id.part_number != 0x09 or self.part_id.revision != 0x02:
            raise RuntimeError("LTR559 not found")

        self._ltr559.set('ALS_CONTROL', sw_reset=1)

        t_start = time.time()
        while time.time() - t_start < timeout:
            status = self._ltr559.get('ALS_CONTROL').sw_reset
            if status == 0:
                break
            time.sleep(0.05)

        if self._ltr559.get('ALS_CONTROL').sw_reset:
            raise RuntimeError("Timeout waiting for software reset.")

        if enable_interrupts:
            self._ltr559.set('INTERRUPT',
                             mode='als+ps',
                             polarity=interrupt_pin_polarity)

        # FIXME use datasheet defaults or document
        self._ltr559.set('PS_LED',
                         current_ma=50,
                         duty_cycle=1.0,
                         pulse_freq_khz=30)

        self._ltr559.set('PS_N_PULSES', count=1)

        self._ltr559.set('ALS_CONTROL',
                         mode=1,
                         gain=self._gain)

        self._ltr559.set('PS_CONTROL',
                         active=True,
                         saturation_indicator_enable=1)

        self._ltr559.set('PS_MEAS_RATE', rate_ms=100)

        self._ltr559.set('ALS_MEAS_RATE',
                         integration_time_ms=self._integration_time,
                         repeat_rate_ms=50)

        self._ltr559.set('ALS_THRESHOLD',
                         lower=0x0000,
                         upper=0xFFFF)

        self._ltr559.set('PS_THRESHOLD',
                         lower=0x0000,
                         upper=0xFFFF)

        self._ltr559.set('PS_OFFSET', offset=0)

    def get_part_id(self):
        """Get part number"""
        return self.part_id.part_number

    def get_revision(self):
        """Get revision ID"""
        return self.part_id.revision

    def set_light_threshold(self, lower, upper):
        """Set light interrupt threshold

        :param lower: Lower threshold
        :param upper: Upper threshold

        """
        self._ltr559.set('ALS_THRESHOLD',
                         lower=lower,
                         upper=upper)

    def set_proximity_threshold(self, lower, upper):
        """Set proximity interrupt threshold

        :param lower: Lower threshold
        :param upper: Upper threshold

        """
        self._ltr559.set('PS_THRESHOLD',
                         lower=lower,
                         upper=upper)

    def set_proximity_rate_ms(self, rate_ms):
        """Set proximity measurement repeat rate in milliseconds

        :param rate_ms: Time in milliseconds- one of 10, 50, 70, 100, 200, 500, 1000 or 2000

        """
        self._ltr559.set('PS_MEAS_RATE', rate_ms)

    def set_light_integration_time_ms(self, time_ms):
        """Set light integration time in milliseconds

        :param time_ms: Time in milliseconds- one of 50, 100, 150, 200, 300, 350, 400

        """
        self._integration_time = time_ms
        self._ltr559.set('ALS_MEAS_RATE', integration_time_ms=time_ms)

    def set_light_repeat_rate_ms(self, rate_ms=100):
        """Set light measurement repeat rate in milliseconds

        :param rate_ms: Rate in milliseconds- one of 50, 100, 200, 500, 1000 or 2000

        """
        self._ltr559.set('ALS_MEAS_RATE', set_repeat_rate_ms=rate_ms)

    def set_interrupt_mode(self, enable_light=True, enable_proximity=True):
        """Set the intterupt mode

        :param enable_light: Enable the light sensor interrupt
        :param enable_proximity: Enable the proximity sensor interrupt

        """
        mode = []

        if enable_light:
            mode.append('als')

        if enable_proximity:
            mode.append('ps')

        self._ltr559.set('INTERRUPT', mode='+'.join(mode))

    def set_proximity_active(self, active=True):
        """Enable/disable proximity sensor

        :param active: True for enabled, False for disabled

        """
        self._ltr559.set('PS_CONTROL', set_active=active)

    def set_proximity_saturation_indictator(self, enabled=True):
        """Enable/disable the proximity saturation indicator

        :param enabled: True for enabled, False for disabled

        """
        self._ltr559.set('PS_CONTROL', saturation_indicator_enable=enabled)

    def set_proximity_offset(self, offset):
        """Setup the proximity compensation offset

        :param offset: Offset value from 0 to 1023

        """
        return self._ltr559.set('PS_OFFSET', offset=offset)

    def set_proximity_led(self, current_ma=50, duty_cycle=1.0, pulse_freq_khz=30, num_pulses=1):
        """Setup the proximity led current and properties

        :param current_ma: LED current in milliamps- one of 5, 10, 20, 50 or 100
        :param duty_cycle: LED duty cucle- one of 0.25, 0.5, 0.75 or 1.0 (25%, 50%, 75% or 100%)
        :param pulse_freq_khz: LED pulse frequency- one of 30, 40, 50, 60, 70, 80, 90 or 100
        :param num_pulse: Number of LED pulses to be emitted- 1 to 15

        """
        self._ltr559.set('PS_LED',
                         current_ma=current_ma,
                         duty_cycle=duty_cycle,
                         set_pulse_freq_khz=pulse_freq_khz)

        self._ltr559.set('PS_N_PULSES', num_pulses)

    def set_light_options(self, active=True, gain=4):
        """Set the mode and gain for the light sensor

        :param active: True for Active Mode, False for Stand-by Mode
        :param gain: Light sensor gain x- one of 1, 2, 4, 8, 48 or 96

        1x = 1 to 64k lux
        2x = 0.5 to 32k lux
        4x = 0.25 to 16k lux
        8x = 0.125 to 8k lux
        48x = 0.02 to 1.3k lux
        96x = 0.01 to 600 lux
        """
        self._gain = gain
        self._ltr559.set('ALS_CONTROL',
                         mode=active,
                         gain=gain)

    def update_sensor(self):
        """Update the sensor lux and proximity values"""
        status = self._ltr559.get('ALS_PS_STATUS')
        ps_int = status.ps_interrupt or status.ps_data
        als_int = status.als_interrupt or status.als_data

        if ps_int:
            self._ps0 = self._ltr559.get('PS_DATA').ch0

        if als_int:
            als = self._ltr559.get('ALS_DATA')
            self._als0 = als.ch0
            self._als1 = als.ch1

            self._ratio = self._als1 * 100 / (self._als1 + self._als0) if self._als0 + self._als1 > 0 else 101

            if self._ratio < 45:
                ch_idx = 0
            elif self._ratio < 64:
                ch_idx = 1
            elif self._ratio < 85:
                ch_idx = 2
            else:
                ch_idx = 3

            try:
                self._lux = (self._als0 * self._ch0_c[ch_idx]) - (self._als1 * self._ch1_c[ch_idx])
                self._lux /= (self._integration_time / 100.0)
                self._lux /= self._gain
                self._lux /= 10000.0
            except ZeroDivisionError:
                self._lux = 0

    def get_gain(self):
        """ Return gain used in lux calculation"""
        return self._gain

    def get_integration_time(self):
        """ Return integration time used in lux calculation"""
        return self._integration_time

    get_intt = get_integration_time

    def get_raw_als(self, passive=True):
        """ reurtn raw ALS channel data ch0,ch1 """
        if not passive:
            self.update_sensor()
        return self._als0, self._als1

    def get_ratio(self, passive=True):
        """Return the ambient light ratio between ALS channels"""
        if not passive:
            self.update_sensor()
        return self._ratio

    def get_lux(self, passive=False):
        """Return the ambient light value in lux"""
        if not passive:
            self.update_sensor()
        return self._lux

    def get_interrupt(self):
        """Return the light and proximity sensor interrupt status"""
        interrupt = self._ltr559.get('ALS_PS_STATUS')
        return interrupt.als_interrupt, interrupt.ps_interrupt

    def get_proximity(self, passive=False):
        """Return the proximity"""
        if not passive:
            self.update_sensor()
        return self._ps0
Beispiel #16
0
class AS7262:
    def __init__(self, i2c_dev=None):
        self._as7262 = Device(
            0x49,
            i2c_dev=as7262VirtualRegisterBus(i2c_dev=i2c_dev),
            bit_width=8,
            registers=(
                Register('VERSION',
                         0x00,
                         fields=(
                             BitField('hw_type', 0xFF000000),
                             BitField('hw_version', 0x00FF0000),
                             BitField('fw_version',
                                      0x0000FFFF,
                                      adapter=FWVersionAdapter()),
                         ),
                         bit_width=32,
                         read_only=True),
                Register('CONTROL',
                         0x04,
                         fields=(
                             BitField('reset', 0b10000000),
                             BitField('interrupt', 0b01000000),
                             BitField('gain_x',
                                      0b00110000,
                                      adapter=LookupAdapter({
                                          1: 0b00,
                                          3.7: 0b01,
                                          16: 0b10,
                                          64: 0b11
                                      })),
                             BitField('measurement_mode', 0b00001100),
                             BitField('data_ready', 0b00000010),
                         )),
                Register('INTEGRATION_TIME',
                         0x05,
                         fields=(BitField(
                             'ms', 0xFF, adapter=IntegrationTimeAdapter()), )),
                Register('TEMPERATURE',
                         0x06,
                         fields=(BitField('degrees_c', 0xFF), )),
                Register('LED_CONTROL',
                         0x07,
                         fields=(
                             BitField('illumination_current_limit_ma',
                                      0b00110000,
                                      adapter=LookupAdapter({
                                          12.5: 0b00,
                                          25: 0b01,
                                          50: 0b10,
                                          100: 0b11
                                      })),
                             BitField('illumination_enable', 0b00001000),
                             BitField('indicator_current_limit_ma',
                                      0b00000110,
                                      adapter=LookupAdapter({
                                          1: 0b00,
                                          2: 0b01,
                                          4: 0b10,
                                          8: 0b11
                                      })),
                             BitField('indicator_enable', 0b00000001),
                         )),
                Register('DATA',
                         0x08,
                         fields=(
                             BitField('v', 0xFFFF00000000000000000000),
                             BitField('b', 0x0000FFFF0000000000000000),
                             BitField('g', 0x00000000FFFF000000000000),
                             BitField('y', 0x000000000000FFFF00000000),
                             BitField('o', 0x0000000000000000FFFF0000),
                             BitField('r', 0x00000000000000000000FFFF),
                         ),
                         bit_width=96),
                Register('CALIBRATED_DATA',
                         0x14,
                         fields=(
                             BitField('v',
                                      0xFFFFFFFF << (32 * 5),
                                      adapter=FloatAdapter()),
                             BitField('b',
                                      0xFFFFFFFF << (32 * 4),
                                      adapter=FloatAdapter()),
                             BitField('g',
                                      0xFFFFFFFF << (32 * 3),
                                      adapter=FloatAdapter()),
                             BitField('y',
                                      0xFFFFFFFF << (32 * 2),
                                      adapter=FloatAdapter()),
                             BitField('o',
                                      0xFFFFFFFF << (32 * 1),
                                      adapter=FloatAdapter()),
                             BitField('r',
                                      0xFFFFFFFF << (32 * 0),
                                      adapter=FloatAdapter()),
                         ),
                         bit_width=192),
            ))

        # TODO : Integrate into i2cdevice so that LookupAdapter fields can always be exported to constants
        # Iterate through all register fields and export their lookup tables to constants
        for register in self._as7262.registers:
            register = self._as7262.registers[register]
            for field in register.fields:
                field = register.fields[field]
                if isinstance(field.adapter, LookupAdapter):
                    for key in field.adapter.lookup_table:
                        value = field.adapter.lookup_table[key]
                        name = 'AS7262_{register}_{field}_{key}'.format(
                            register=register.name, field=field.name,
                            key=key).upper()
                        locals()[name] = key

        self.soft_reset()

    def soft_reset(self):
        """Set the soft reset register bit of the AS7262."""
        self._as7262.set('CONTROL', reset=1)
        # Polling for the state of the reset flag does not work here
        # since the fragile virtual register state machine cannot
        # respond while in a soft reset condition
        # So, just wait long enough for it to reset fully...
        time.sleep(2.0)

    def get_calibrated_values(self, timeout=10):
        """Return an instance of CalibratedValues containing the 6 spectral bands."""
        t_start = time.time()
        while self._as7262.get('CONTROL').data_ready == 0 and (
                time.time() - t_start) <= timeout:
            pass
        data = self._as7262.get('CALIBRATED_DATA')
        return CalibratedValues(data.r, data.o, data.y, data.g, data.b, data.v)

    def set_gain(self, gain):
        """Set the gain amount of the AS7262.

        :param gain: gain multiplier, one of 1, 3.7, 16 or 64

        """
        self._as7262.set('CONTROL', gain_x=gain)

    def set_measurement_mode(self, mode):
        """Set the AS7262 measurement mode.

        :param mode: 0-3

        """
        self._as7262.set('CONTROL', measurement_mode=mode)

    def set_integration_time(self, time_ms):
        """Set the AS7262 sensor integration time in milliseconds.

        :param time_ms: Time in milliseconds from 0 to ~91

        """
        self._as7262.set('INTEGRATION_TIME', ms=time_ms)

    def set_illumination_led_current(self, current):
        """Set the AS7262 illumination LED current in milliamps.

        :param current: Value in milliamps, one of 12.5, 25, 50 or 100

        """
        self._as7262.set('LED_CONTROL', illumination_current_limit_ma=current)

    def set_indicator_led_current(self, current):
        """Set the AS7262 indicator LED current in milliamps.

        :param current: Value in milliamps, one of 1, 2, 4 or 8

        """
        self._as7262.set('LED_CONTROL', indicator_current_limit_ma=current)

    def set_illumination_led(self, state):
        """Set the AS7262 illumination LED state.

        :param state: True = On, False = Off

        """
        self._as7262.set('LED_CONTROL', illumination_enable=state)

    def set_indicator_led(self, state):
        """Set the AS7262 indicator LED state.

        :param state: True = On, False = Off

        """
        self._as7262.set('LED_CONTROL', indicator_enable=state)

    def get_version(self):
        """Get the hardware type, version and firmware version from the AS7262."""
        version = self._as7262.get('VERSION')
        return version.hw_type, version.hw_version, version.fw_version
Beispiel #17
0
class RV3028:
    def __init__(self, i2c_addr=0x26, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._is_setup = False

        self._rv3028 = Device([0x52], i2c_dev=self._i2c_dev, bit_width=8, registers=(
            Register('SECONDS', 0x00, fields=(
                BitField('seconds', 0x7F, adapter=BCDAdapter()),
            )),
            Register('MINUTES', 0x01, fields=(
                BitField('minutes', 0x7F, adapter=BCDAdapter()),
            )),
            Register('HOURS', 0x02, fields=(
                BitField('t24hours', 0b00111111, adapter=BCDAdapter()),
                BitField('t12hours', 0b00011111, adapter=BCDAdapter()),
                BitField('am_pm', 0b00100000),
            )),
            Register('WEEKDAY', 0x03, fields=(
                BitField('weekday', 0b00000111),
            )),
            Register('DATE', 0x04, fields=(
                BitField('date', 0b00111111, adapter=BCDAdapter()),
            )),
            Register('MONTH', 0x05, fields=(
                BitField('month', 0b00011111, adapter=BCDAdapter()),
            )),
            Register('YEAR', 0x06, fields=(
                BitField('year', 0xFF, adapter=BCDAdapter()),
            )),
            Register('ALARM_MINUTES', 0x07, fields=(
                BitField('minutes_alarm_enable', 0b10000000),
                BitField('minutes', 0x7F, adapter=BCDAdapter()),
            )),
            Register('ALARM_HOURS', 0x08, fields=(
                BitField('hours_alarm_enable', 0b10000000, adapter=BCDAdapter()),
                BitField('t24hours', 0b00111111, adapter=BCDAdapter()),
                BitField('t12hours', 0b00011111, adapter=BCDAdapter()),
                BitField('am_pm', 0b00100000),
            )),
            Register('ALARM_WEEKDAY', 0x09, fields=(
                BitField('weekday_alarm_enable', 0b10000000, adapter=BCDAdapter()),
                BitField('weekday', 0b00000111),
                BitField('date', 0b00111111)
            )),
            Register('TIMER_VALUE', 0x0A, fields=(
                BitField('value', 0xFF0F, adapter=U16ByteSwapAdapter()),
            ), bit_width=16),
            Register('TIMER_STATUS', 0x0C, fields=(
                BitField('value', 0xFF0F, adapter=U16ByteSwapAdapter()),
            ), bit_width=16),
            Register('STATUS', 0x0E, fields=(
                BitField('value', 0xFF),
                BitField('eeprom_busy_flag', 0b10000000),
                BitField('clock_output_interrupt_flag', 0b01000000),
                BitField('backup_switch_flag', 0b00100000),
                BitField('periodic_time_update_flag', 0b00010000),
                BitField('periodic_countdown_timer_flag', 0b00001000),
                BitField('alarm_flag', 0b00000100),
                BitField('external_event_flag', 0b00000010),
                BitField('power_on_reset_flag', 0b00000001),
            )),
            Register('CONTROL_1', 0x0F, fields=(
                BitField('value', 0xFF),
                BitField('timer_repeat', 0b10000000),
                BitField('weekday_date_alarm', 0b00100000),
                BitField('update_interrupt', 0b00010000),
                BitField('eeprom_memory_refresh_disable', 0b00001000),
                BitField('periodic_countdown_timer_enable', 0b00000100),
                BitField('timer_frequency_selection', 0b00000011, adapter=LookupAdapter({
                    '4036Hz': 0b00,  # 214.14us
                    '63Hz': 0b01,    # 15.625ms
                    '1Hz': 0b10,     # 1s
                    '0.016Hz': 0b11  # 60s
                })),
            )),
            Register('CONTROL_2', 0x10, fields=(
                BitField('value', 0xFF),
                BitField('timestamp_enable', 0b10000000),
                BitField('interrupt_controlled_output_enable', 0b01000000),
                BitField('periodic_time_update_interupt_enable', 0b00100000),
                BitField('periodic_countdown_timer_interupt_enable', 0b00010000),
                BitField('alarm_interupt_enable', 0b00001000),
                BitField('external_event_interrupt_enable', 0b00000100),
                BitField('select_24_12_hours', 0b00000010),
                BitField('reset', 0b00000001),
            )),
            Register('GENERAL_PURPOSE_STORAGE_REGISTER', 0x11, fields=(
                BitField('value', 0b01111111),
            )),
            Register('CLOCK_INTERRUPT_MASK', 0x12, fields=(
                BitField('value', 0x0F),
                BitField('clock_output_when_event_interrupt', 0b00001000),
                BitField('clock_output_when_alarm_interrupt', 0b00000100),
                BitField('clock_output_when_countdown_interrupt', 0b00000010),
                BitField('clock_output_when_periodic_interrupt', 0b00000001),
            )),
            Register('EVENT_CONTROL', 0x13, fields=(
                BitField('value', 0xFF),
                BitField('event_high_low_detection', 0b01000000),
                BitField('event_filtering_time', 0b00110000, adapter=LookupAdapter({
                    'no_filtering': 0b00,
                    '3.9ms': 0b01,
                    '15.6ms': 0b10,
                    '125ms': 0b11
                })),
                BitField('timestamp_reset', 0b00000100),
                BitField('timestamp_overwrite', 0b00000010),
                BitField('timestamp_source', 0b00000001),
            )),
            Register('TIMESTAMP_COUNT', 0x14, fields=(
                BitField('value', 0xFF),
            )),
            Register('TIMESTAMP_SECONDS', 0x15, fields=(
                BitField('seconds', 0x7F),
            )),
            Register('TIMESTAMP_MINUTES', 0x16, fields=(
                BitField('minutes', 0x7F),
            )),
            Register('TIMESTAMP_HOURS', 0x17, fields=(
                BitField('t24hours', 0b00111111),
                BitField('t12hours', 0b00001111),
                BitField('am_pm', 0x00010000),
            )),
            Register('TIMESTAMP_DATE', 0x18, fields=(
                BitField('date', 0b00011111),
            )),
            Register('TIMESTAMP_MONTH', 0x19, fields=(
                BitField('month', 0b00001111),
            )),
            Register('TIMESTAMP_YEAR', 0x1A, fields=(
                BitField('year', 0xFF),
            )),
            Register('UNIX_TIME', 0x1B, fields=(
                BitField('value', 0xFFFFFFFF, adapter=ReverseBytesAdapter(4)),
            ), bit_width=32),
            Register('USER_RAM', 0x1F, fields=(
                BitField('one', 0x00FF),
                BitField('two', 0xFF00),
            ), bit_width=16),
            Register('PASSWORD', 0x21, fields=(
                BitField('value', 0xFFFFFFFF),
            ), bit_width=32),
            Register('EEPROM_ADDRESS', 0x25, fields=(
                BitField('value', 0xFF),
            )),
            Register('EEPROM_DATA', 0x26, fields=(
                BitField('value', 0xFF),
            )),
            Register('EEPROM_COMMAND', 0x27, fields=(
                BitField('command', 0xFF, adapter=LookupAdapter({
                    'first_command': 0x00,
                    'write_all_configuration_to_eeprom': 0x11,
                    'read_all_configuration_from_eeprom': 0x12,
                    'write_one_byte_to_eeprom_address': 0x21,
                    'read_one_byte_from_eeprom_address': 0x22
                })),
            )),
            Register('PART', 0x28, fields=(
                BitField('id', 0xF0),
                BitField('version', 0x0F)
            )),
            Register('EEPROM_PASSWORD_ENABLE', 0x30, fields=(
                BitField('value', 0xFF),  # Write 0xFF to this register to enable EEPROM password
            )),
            Register('EEPROM_PASSWORD', 0x31, fields=(
                BitField('value', 0xFFFFFFFF),
            ), bit_width=32),
            Register('EEPROM_CLKOUT', 0x35, fields=(
                BitField('value', 0xFF),
                BitField('clkout_output', 0b10000000),
                BitField('clkout_synchronized', 0b01000000),
                BitField('power_on_reset_interrupt_enable', 0b00001000),
                BitField('clkout_frequency_selection', 0b00000111, adapter=LookupAdapter({
                    '32.768kHz': 0b000,
                    '8192Hz': 0b001,
                    '1024Hz': 0b010,
                    '64Hz': 0b011,
                    '32Hz': 0b100,
                    '1Hz': 0b101,
                    'periodic_countdown_timer_interrupt': 0b110,
                    'clkout_low': 0b111,
                })),
            )),
            Register('EEPROM_OFFSET', 0x36, fields=(
                BitField('value', 0xFF),
            )),
            Register('EEPROM_BACKUP', 0x37, fields=(
                BitField('value', 0xFF),
                BitField('ee_offset', 0b10000000),
                BitField('backup_switchover_interrupt_enable', 0b01000000),
                BitField('trickle_charger_enable', 0b00100000),
                BitField('fast_edge_detection', 0b00010000),
                BitField('automatic_battery_switchover', 0b00001100, adapter=LookupAdapter({
                    'switchover_disabled': 0b00,
                    'direct_switching_mode': 0b01,
                    'standby_mode': 0b10,
                    'level_switching_mode': 0b11
                })),
                BitField('trickle_charger_series_resistance', 0b00000011, adapter=LookupAdapter({
                    '1kOhm': 0b00,
                    '3kOhm': 0b01,
                    '6kOhm': 0b10,
                    '11kOhm': 0b11
                })),
            ))


        ))
        self.enable_12hours = self._rv3028.get('CONTROL_2').select_24_12_hours
        self.alarm_frequency = {
            'disabled_weekly': 0b0111,
            'disabled_monthly': 0b1111,
            'hourly_on_minute': 0b110,
            'daily_on_hour': 0b101,
            'daily_on_hour_and_minute': 0b011,
            'weekly': 0b0011,
            'weekly_on_minute': 0b0010,
            'weekly_on_hour': 0b0001,
            'weekly_on_hour_and_minute': 0b0000,
            'monthly': 0b1011,
            'monthly_on_minute': 0b1010,
            'monthly_on_hour': 0b1001,
            'monthly_on_hour_and_minute': 0b1000
        }

    def reset(self):
        self._rv3028.set('CONTROL_2', reset=True)
        time.sleep(0.01)

    def get_id(self):
        part = self._rv3028.get('PART')
        return part.id, part.version

    def get_time(self):
        datetime_object = self.get_time_and_date()
        return datetime_object.time()

    def set_time(self, t):
        if isinstance(t, datetime.datetime) or isinstance(t, datetime.time):
            self._rv3028.set('HOURS', t24hours=t.hour)
            self._rv3028.set('MINUTES', minutes=t.minute)
            self._rv3028.set('SECONDS', seconds=t.second)
        elif type(t) == tuple:
            self._rv3028.set('HOURS', t24hours=t[0])
            self._rv3028.set('MINUTES', minutes=t[1])
            self._rv3028.set('SECONDS', seconds=t[2])
        else:
            raise TypeError('Time needs to be given as datetime.datetime object, or tuple (hour, minute, seconds) type used: {0}'.format(type(t)))

    def get_date(self):
        datetime_object = self.get_time_and_date()
        return datetime_object.date()

    def set_date(self, date):
        if isinstance(date, datetime.datetime) or isinstance(date, datetime.date):
            self._rv3028.set('YEAR', year=date.year - 2000)
            self._rv3028.set('MONTH', month=date.month)
            self._rv3028.set('DATE', date=date.day)
        elif type(date) == tuple:
            self._rv3028.set('YEAR', year=date[0] - 2000)
            self._rv3028.set('MONTH', month=date[1])
            self._rv3028.set('DATE', date=date[2])
        else:
            raise TypeError('Date needs to be given as datetime.datetime object, datetime.date object, or tuple (year, month, day) type used: {0}'.format(type(date)))

    def set_time_and_date(self, time_and_date):
        if isinstance(time_and_date, datetime.datetime):
            self.set_date(time_and_date)
            self.set_time(time_and_date)

        elif type(time_and_date) == tuple:
            self.set_date(time_and_date[:3])
            self.set_time(time_and_date[3:])

        else:
            raise TypeError('Time needs to be given as datetime.datetime object, or tuple (year, month, day, hour, minute, seconds) type used: {0}'.format(type(time_and_date)))

    def get_time_and_date(self):
        return datetime.datetime(
            self._rv3028.get('YEAR').year + 2000,
            self._rv3028.get('MONTH').month,
            self._rv3028.get('DATE').date,
            self._rv3028.get('HOURS').t24hours,
            self._rv3028.get('MINUTES').minutes,
            self._rv3028.get('SECONDS').seconds)

    def get_unix_time(self):
        return self._rv3028.get('UNIX_TIME').value

    def set_unix_time(self, value):
        self._rv3028.set('UNIX_TIME', value=value)

    def set_battery_switchover(self, value):
        self._rv3028.set('EEPROM_BACKUP', automatic_battery_switchover=value)

    def get_battery_switchover(self):
        return self._rv3028.get('EEPROM_BACKUP').automatic_battery_switchover

    def start_periodic_timer(self):
        self._rv3028.set('CONTROL_1', periodic_countdown_timer_enable=True)

    def stop_periodic_timer(self):
        self._rv3028.set('CONTROL_1', periodic_countdown_timer_enable=False)

    def get_periodic_timer_frequency(self):
        return self._rv3028.get('CONTROL_1').timer_frequency_selection

    def set_periodic_timer_frequency(self, value):
        self._rv3028.set('CONTROL_1', timer_frequency_selection=value)

    def set_periodic_timer_countdown_value(self, value):
        self._rv3028.set('TIMER_VALUE', value=value)

    def get_periodic_timer_countdown_value(self):
        return self._rv3028.get('TIMER_VALUE').value

    def get_periodic_timer_countdown_status(self):
        return self._rv3028.get('TIMER_STATUS').value

    def clear_all_interrupts(self):
        self._rv3028.set('STATUS', value=0)

    def clear_periodic_countdown_timer_interrupt(self):
        self._rv3028.set('STATUS', periodic_countdown_timer_flag=0)

    def clear_alarm_interrupt(self):
        self._rv3028.set('STATUS', alarm_flag=0)

    def get_all_interrupts(self):
        return self._rv3028.get('STATUS').value

    def get_periodic_countdown_timer_interrupt(self):
        return self._rv3028.get('STATUS').periodic_countdown_timer_flag

    def get_alarm_interrupt(self):
        return self._rv3028.get('STATUS').alarm_flag

    def wait_for_periodic_timer_interrupt(self, value):
        """Wait for a periodic timer countdown.

        The countdown period in seconds is equal to the value/timer frequency.

        """
        self.stop_periodic_timer()
        self._rv3028.set('TIMER_VALUE', value=value)
        self._rv3028.set('STATUS', periodic_countdown_timer_flag=False)
        self.start_periodic_timer()
        while not self._rv3028.get('STATUS').periodic_countdown_timer_flag:
            time.sleep(0.001)

    def get_alarm_setting(self):
        setting = self._rv3028.get('ALARM_MINUTES').minutes_alarm_enable
        setting |= (self._rv3028.get('ALARM_HOURS').hours_alarm_enable << 1)
        setting |= (self._rv3028.get('ALARM_WEEKDAY').weekday_alarm_enable << 2)
        setting |= (self._rv3028.get('CONTROL_1').weekday_date_alarm << 3)
        return_value = [key for (key, value) in self.alarm_frequency.items() if value == setting]
        return return_value

    def set_alarm_setting(self, setting):
        self._rv3028.set('ALARM_MINUTES', minutes_alarm_enable=self.alarm_frequency.get(setting) & 0b0001)
        self._rv3028.set('ALARM_HOURS', hours_alarm_enable=(self.alarm_frequency.get(setting) & 0b0010) >> 1)
        self._rv3028.set('ALARM_WEEKDAY', weekday_alarm_enable=(self.alarm_frequency.get(setting) & 0b0100) >> 2)
        self._rv3028.set('CONTROL_1', weekday_date_alarm=(self.alarm_frequency.get(setting) & 0b1000) >> 3)

    def set_alarm_time(self, datetime_object, weekday=0):
        if weekday == 0:
            if isinstance(datetime_object, datetime.datetime):
                self._rv3028.set('ALARM_WEEKDAY', date=datetime_object.day)
                self._rv3028.set('ALARM_HOURS', t24hours=datetime_object.hour)
                self._rv3028.set('ALARM_MINUTES', minutes=datetime_object.minute)

            elif type(datetime_object) == tuple:
                self._rv3028.set('ALARM_WEEKDAY', date=datetime_object[0])
                self._rv3028.set('ALARM_HOURS', t24hours=datetime_object[1])
                self._rv3028.set('ALARM_MINUTES', minutes=datetime_object[2])

            else:
                raise TypeError('Time needs to be given as datetime.datetime object or tuple (hour, minute, date) type used: {0}'.format(type(time)))
        else:
            if isinstance(datetime_object, datetime.datetime):
                self._rv3028.set('ALARM_WEEKDAY', weekday=weekday)
                self._rv3028.set('ALARM_HOURS', t24hours=datetime_object.hour)
                self._rv3028.set('ALARM_MINUTES', minutes=datetime_object.minute)

            elif type(datetime_object) == tuple:
                self._rv3028.set('ALARM_WEEKDAY', weekday=weekday)
                self._rv3028.set('ALARM_HOURS', t24hours=datetime_object[0])
                self._rv3028.set('ALARM_MINUTES', minutes=datetime_object[1])
            else:
                raise TypeError('Time needs to be given as datetime.datetime object or tuple (hour, minute) and a 0 > weekday int type used: {0}'.format(type(time)))

    def get_alarm_time(self):
        return datetime.time(
            self._rv3028.get('ALARM_HOURS').t24hours,
            self._rv3028.get('ALARM_MINUTES').minutes,
            self._rv3028.get('ALARM_WEEKDAY').weekday)
Beispiel #18
0
class BH1745:
    """BH1745 colour sensor."""

    def __init__(self, i2c_addr=0x38, i2c_dev=None):
        """Initialise sensor.

        :param i2c_addr: i2c address of sensor
        :param i2c_dev: SMBus-compatible instance

        """
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._is_setup = False
        # Device definition
        self._bh1745 = Device(I2C_ADDRESSES, i2c_dev=self._i2c_dev, bit_width=8, registers=(
            # Part ID should be 0b001011 or 0x0B
            Register('SYSTEM_CONTROL', 0x40, fields=(
                BitField('sw_reset', 0b10000000),
                BitField('int_reset', 0b01000000),
                BitField('part_id', 0b00111111, read_only=True)
            )),

            Register('MODE_CONTROL1', 0x41, fields=(
                BitField('measurement_time_ms', 0b00000111, adapter=LookupAdapter({
                    160: 0b000,
                    320: 0b001,
                    640: 0b010,
                    1280: 0b011,
                    2560: 0b100,
                    5120: 0b101
                })),
            )),

            Register('MODE_CONTROL2', 0x42, fields=(
                BitField('valid', 0b10000000, read_only=True),
                BitField('rgbc_en', 0b00010000),
                BitField('adc_gain_x', 0b00000011, adapter=LookupAdapter({
                    1: 0b00, 2: 0b01, 16: 0b10}))
            )),

            Register('MODE_CONTROL3', 0x44, fields=(
                BitField('on', 0b11111111, adapter=LookupAdapter({True: 2, False: 0})),
            )),

            Register('COLOUR_DATA', 0x50, fields=(
                BitField('red', 0xFFFF000000000000, adapter=U16ByteSwapAdapter()),
                BitField('green', 0x0000FFFF00000000, adapter=U16ByteSwapAdapter()),
                BitField('blue', 0x00000000FFFF0000, adapter=U16ByteSwapAdapter()),
                BitField('clear', 0x000000000000FFFF, adapter=U16ByteSwapAdapter())
            ), bit_width=64, read_only=True),

            Register('DINT_DATA', 0x58, fields=(
                BitField('data', 0xFFFF, adapter=U16ByteSwapAdapter()),
            ), bit_width=16),

            Register('INTERRUPT', 0x60, fields=(
                BitField('status', 0b10000000, read_only=True),
                BitField('latch', 0b00010000, adapter=LookupAdapter({0: 1, 1: 0})),
                BitField('source', 0b00001100, read_only=True, adapter=LookupAdapter({
                    'red': 0b00,
                    'green': 0b01,
                    'blue': 0b10,
                    'clear': 0b11
                })),
                BitField('enable', 0b00000001)
            )),

            # 00: Interrupt status is toggled at each measurement end
            # 01: Interrupt status is updated at each measurement end
            # 10: Interrupt status is updated if 4 consecutive threshold judgements are the same
            # 11: Blah blah ditto above except for 8 consecutive judgements
            Register('PERSISTENCE', 0x61, fields=(
                BitField('mode', 0b00000011, adapter=LookupAdapter({
                    'toggle': 0b00,
                    'update': 0b01,
                    'update_on_4': 0b10,
                    'update_on_8': 0b11
                })),
            )),

            # High threshold defaults to 0xFFFF
            # Low threshold defaults to 0x0000
            Register('THRESHOLD', 0x62, fields=(
                BitField('high', 0xFFFF0000, adapter=U16ByteSwapAdapter()),
                BitField('low', 0x0000FFFF, adapter=U16ByteSwapAdapter())
            ), bit_width=32),

            # Default MANUFACTURER ID is 0xE0h
            Register('MANUFACTURER', 0x92, fields=(
                BitField('id', 0xFF),
            ), read_only=True, volatile=False)
        ))

        self._bh1745.select_address(self._i2c_addr)

        # TODO : Integrate into i2cdevice so that LookupAdapter fields can always be exported to constants
        # Iterate through all register fields and export their lookup tables to constants
        for register in self._bh1745.registers:
            register = self._bh1745.registers[register]
            for field in register.fields:
                field = register.fields[field]
                if isinstance(field.adapter, LookupAdapter):
                    for key in field.adapter.lookup_table:
                        name = 'BH1745_{register}_{field}_{key}'.format(
                            register=register.name,
                            field=field.name,
                            key=key
                        ).upper()
                        globals()[name] = key

        """
        Approximate compensation for the spectral response performance curves
        """
        self._channel_compensation = (2.2, 1.0, 1.8, 10.0)
        self._enable_channel_compensation = True

    # Public API methods
    def ready(self):
        """Return true if setup has been successful."""
        return self._is_setup

    def setup(self, i2c_addr=None, timeout=BH1745_RESET_TIMEOUT_SEC):
        """Set up the bh1745 sensor.

        :param i2c_addr: Optional i2c_addr to switch to

        """
        if self._is_setup:
            return True

        if timeout <= 0:
            raise ValueError('Device timeout period must be greater than 0')

        if i2c_addr is not None:
            self._bh1745.select_address(i2c_addr)

        try:
            self._bh1745.get('SYSTEM_CONTROL')
        except IOError:
            raise RuntimeError('BH1745 not found: IO error attempting to query device!')

        if self._bh1745.get('SYSTEM_CONTROL').part_id != 0b001011 or self._bh1745.get('MANUFACTURER').id != 0xE0:
            raise RuntimeError('BH1745 not found: Manufacturer or Part ID mismatch!')

        self._is_setup = True

        self._bh1745.set('SYSTEM_CONTROL', sw_reset=1)

        t_start = time.time()

        pending_reset = True

        while time.time() - t_start < timeout:
            if not self._bh1745.get('SYSTEM_CONTROL').sw_reset:
                pending_reset = False
                break
            time.sleep(0.01)

        if pending_reset:
            raise BH1745TimeoutError('Timeout waiting for BH1745 to reset.')

        self._bh1745.set('SYSTEM_CONTROL', int_reset=0)
        self._bh1745.set('MODE_CONTROL1', measurement_time_ms=320)
        self._bh1745.set('MODE_CONTROL2', adc_gain_x=1, rgbc_en=1)
        self._bh1745.set('MODE_CONTROL3', on=1)
        self._bh1745.set('THRESHOLD', low=0xFFFF, high=0x0000)
        self._bh1745.set('INTERRUPT', latch=1)

        time.sleep(0.320)

    def set_measurement_time_ms(self, time_ms):
        """Set the measurement time in milliseconds.

        :param time_ms: The time in milliseconds: 160, 320, 640, 1280, 2560, 5120

        """
        self.setup()
        self._bh1745.set('MODE_CONTROL1', measurement_time_ms=time_ms)

    def set_adc_gain_x(self, gain_x):
        """Set the ADC gain multiplier.

        :param gain_x: Must be either 1, 2 or 16

        """
        self.setup()
        self._bh1745.set('MODE_CONTROL2', adc_gain_x=gain_x)

    def set_leds(self, state):
        """Toggle the onboard LEDs.

        :param state: Either 1 for on, or 0 for off

        """
        self.setup()
        self._bh1745.set('INTERRUPT', enable=1 if state else 0)

    def set_channel_compensation(self, r, g, b, c):
        """Set the channel compensation scale factors.

        :param r: multiplier for red channel
        :param g: multiplier for green channel
        :param b: multiplier for blue channel
        :param c: multiplier for clear channel

        If you intend to measure a particular class of objects, say a set of matching wooden blocks with similar reflectivity and paint finish
        you should calibrate the channel compensation until you see colour values that broadly represent the colour of the objects you're testing.

        The default values were derived by testing a set of 5 Red, Green, Blue, Yellow and Orange wooden blocks.

        These scale factors are applied in `get_rgbc_raw` right after the raw values are read from the sensor.

        """
        self._channel_compensation = (r, g, b, c)

    def enable_white_balance(self, enable):
        """Enable scale compensation for the channels.

        :param enable: True to enable, False to disable

        See: `set_channel_compensation` for details.

        """
        self._enable_channel_compensation = True if enable else False

    def get_rgbc_raw(self):
        """Return the raw Red, Green, Blue and Clear readings."""
        self.setup()
        colour_data = self._bh1745.get('COLOUR_DATA')
        r, g, b, c = colour_data.red, colour_data.green, colour_data.blue, colour_data.clear

        if self._enable_channel_compensation:
            cr, cg, cb, cc = self._channel_compensation
            r, g, b, c = r * cr, g * cg, b * cb, c * cc

        return (r, g, b, c)

    def get_rgb_clamped(self):
        """Return an RGB value scaled against max(r, g, b).

        This will clamp/saturate one of the colour channels, providing a clearer idea
        of what primary colour an object is most likely to be.

        However the resulting colour reading will not be accurate for other purposes.

        """
        r, g, b, c = self.get_rgbc_raw()

        div = max(r, g, b)

        if div > 0:
            r, g, b = [int((x / float(div)) * 255) for x in (r, g, b)]
            return (r, g, b)

        return (0, 0, 0)

    def get_rgb_scaled(self):
        """Return an RGB value scaled against the clear channel."""
        r, g, b, c = self.get_rgbc_raw()

        if c > 0:
            r, g, b = [min(255, int((x / float(c)) * 255)) for x in (r, g, b)]
            return (r, g, b)

        return (0, 0, 0)
Beispiel #19
0
class INA220:
    def __init__(self, i2c_addr=0x45, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._is_setup = False

        self.shunt_resistor_value = 0.005  # value in ohms
        self.shunt_voltage_lsb = 0.00001   # 10 uV per LSB
        self.bus_voltage_lsb = 0.004       # 4mV per LSB

        self._ina220 = Device([self._i2c_addr], i2c_dev=self._i2c_dev, bit_width=8, registers=(
            Register('CONFIG', 0x00, fields=(
                BitField('reset', 0b1000000000000000),
                BitField('bus_voltage_range', 0b0010000000000000),
                BitField('pga_gain', 0b0001100000000000, adapter=LookupAdapter({
                    '40': 0b00,
                    '80': 0b01,
                    '160': 0b10,
                    '320': 0b11
                }), bit_width=16),
                BitField('bus_adc', 0b0000011110000000, adapter=sadc_badc_adapter, bit_width=16),
                BitField('shunt_adc', 0b0000000001111000, adapter=sadc_badc_adapter, bit_width=16),
                BitField('mode', 0b0000000000000111, adapter=LookupAdapter({
                    'power_down': 0b000,
                    'shunt_voltage_triggered': 0b001,
                    'bus_voltage_triggered': 0b010,
                    'shunt_and_bus_triggered': 0b011,
                    'adc_off': 0b100,
                    'shunt_voltage_continuous': 0b101,
                    'bus_voltage_continuous': 0b110,
                    'shunt_and_bus_continuous': 0b111
                }), bit_width=16),

            )),

            Register('SHUNT_VOLTAGE', 0x01, fields=(
                BitField('reading', 0xFFFF),
            ), bit_width=16, read_only=True),

            Register('BUS_VOLTAGE', 0x02, fields=(
                BitField('reading', 0b1111111111111000),
                BitField('conversion_ready', 0b0000000000000010),
                BitField('math_overflow_flag', 0b0000000000000001)

            ), bit_width=16, read_only=True),

            Register('POWER', 0x03, fields=(
                BitField('reading', 0xFFFF),
            ), bit_width=16, read_only=True),

            Register('CURRENT', 0x04, fields=(
                BitField('reading', 0xFFFF),
            ), bit_width=16, read_only=True),

            Register('CALIBARTION', 0x05, fields=(
                BitField('reading', 0xFFFF),
            ), bit_width=16, read_only=True),
        ))

        self._configuration = self.get_configuration()

    def get_configuration(self):
        return self._ina220.get('CONFIG')

    def set_bus_voltage_range(self, bus_voltage_range):
        self._ina220.set('CONFIG', bus_voltage_range=bus_voltage_range)

    def get_shunt_voltage(self):
        """Gets the voltage across the shunt resistor."""
        reading = self._ina220.get('SHUNT_VOLTAGE').reading
        if reading & 0x8000:
            reading = -(reading ^ 0xffff)
        return reading * self.shunt_voltage_lsb

    def get_bus_voltage(self):
        """Gets the input (bus) voltage."""
        return self._ina220.get('BUS_VOLTAGE').reading * self.bus_voltage_lsb

    def get_shunt_current(self):
        """Calculates the current across the shunt resistor in Amps."""
        return self.get_shunt_voltage() / self.shunt_resistor_value

    def get_current(self):
        """Gets current calculation from the INA220."""
        return self._ina220.get('CURRENT').reading

    def get_voltage(self):
        """Gets voltage from the INA220."""
        return self._ina220.get('VOLTAGE').reading

    def get_measurements(self):
        shunt_voltage = self.get_shunt_voltage()
        bus_voltage = self.get_bus_voltage()
        current_draw = self.get_shunt_current()

        return current_draw, bus_voltage, shunt_voltage