Пример #1
0
def test_address_select():
    bus = MockSMBus(1)
    device = Device([0x00, 0x01],
                    i2c_dev=bus,
                    registers=(Register('test',
                                        0x00,
                                        fields=(BitField('test', 0xFF), )), ))

    assert device.get_addresses() == [0x00, 0x01]
    assert device.select_address(0x01) is True
    with pytest.raises(ValueError):
        device.select_address(0x02)

    assert device.next_address() == 0x00
    assert device.next_address() == 0x01
Пример #2
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)
Пример #3
0
class LSM303D:
    def __init__(self, i2c_addr=0x1D, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._lsm303d = Device(
            [0x1D, 0x1E],
            i2c_dev=self._i2c_dev,
            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('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('6d_enable', 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('6d_enable', 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
        with self._lsm303d.CONTROL2 as CONTROL2:
            CONTROL2.set_accel_full_scale_g(self._accel_full_scale_g)
            CONTROL2.write()

    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
        with self._lsm303d.CONTROL6 as CONTROL6:
            CONTROL6.set_mag_full_scale_gauss(scale)  # +-2
            CONTROL6.write()

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

        self._lsm303d.select_address(self._i2c_addr)

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

        with self._lsm303d.CONTROL1 as CONTROL1:
            CONTROL1.set_accel_x_enable(1)
            CONTROL1.set_accel_y_enable(1)
            CONTROL1.set_accel_z_enable(1)
            CONTROL1.set_accel_data_rate_hz(50)
            CONTROL1.write()

        self.set_accel_full_scale_g(2)

        with self._lsm303d.INTERRUPT1 as INT1:
            INT1.set_enable_fifo_empty(0)
            INT1.set_enable_accel_dataready(0)
            INT1.set_enable_accelerometer(0)
            INT1.set_enable_magnetometer(0)
            INT1.set_enable_ig2(0)
            INT1.set_enable_ig1(0)
            INT1.set_enable_click(0)
            INT1.set_enable_boot(0)
            INT1.write()

        with self._lsm303d.INTERRUPT2 as INT2:
            INT2.set_enable_fifo(0)
            INT2.set_enable_fifo_overrun(0)
            INT2.set_enable_mag_dataready(0)
            INT2.set_enable_accel_dataready(0)
            INT2.set_enable_magnetometer(0)
            INT2.set_enable_ig2(0)
            INT2.set_enable_ig1(0)
            INT2.set_enable_click(0)
            INT2.write()

        with self._lsm303d.CONTROL5 as CONTROL5:
            CONTROL5.set_mag_data_rate_hz(50)
            CONTROL5.set_enable_temperature(1)
            CONTROL5.write()

        self.set_mag_full_scale_guass(2)

        with self._lsm303d.CONTROL7 as CONTROL7:
            CONTROL7.set_mag_mode('continuous')
            CONTROL7.write()

    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()
        with self._lsm303d.MAGNETOMETER as M:
            x, y, z = M.get_x(), M.get_y(), M.get_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()
        with self._lsm303d.ACCELEROMETER as A:
            x, y, z = A.get_x(), A.get_y(), A.get_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.TEMPERATURE.get_temperature()
Пример #4
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
Пример #5
0
class BME280_new:
    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:
            if self._bme280.CHIP_ID.get_id() != CHIP_ID:
                raise RuntimeError(
                    "Unable to find bme280 on 0x{:02x}, CHIP_ID returned {:02x}"
                    .format(self._i2c_addr, self._bme280.CHIP_ID.get_id()))
        except IOError:
            raise RuntimeError(
                "Unable to find bme280 on 0x{:02x}, IOError".format(
                    self._i2c_addr))

        self._bme280.RESET.set_reset(0xB6)
        time.sleep(0.1)

        self._bme280.CTRL_HUM.set_osrs_h(humidity_oversampling)

        with self._bme280.CTRL_MEAS as CTRL_MEAS:
            CTRL_MEAS.set_mode(mode)
            CTRL_MEAS.set_osrs_t(temperature_oversampling)
            CTRL_MEAS.set_osrs_p(pressure_oversampling)
            CTRL_MEAS.write()

        with self._bme280.CONFIG as CONFIG:
            CONFIG.set_t_sb(temperature_standby)
            CONFIG.set_filter(2)
            CONFIG.write()

        with self._bme280.CALIBRATION as CALIBRATION:
            self.calibration.dig_t1 = CALIBRATION.get_dig_t1()
            self.calibration.dig_t2 = CALIBRATION.get_dig_t2()
            self.calibration.dig_t3 = CALIBRATION.get_dig_t3()

            self.calibration.dig_p1 = CALIBRATION.get_dig_p1()
            self.calibration.dig_p2 = CALIBRATION.get_dig_p2()
            self.calibration.dig_p3 = CALIBRATION.get_dig_p3()
            self.calibration.dig_p4 = CALIBRATION.get_dig_p4()
            self.calibration.dig_p5 = CALIBRATION.get_dig_p5()
            self.calibration.dig_p6 = CALIBRATION.get_dig_p6()
            self.calibration.dig_p7 = CALIBRATION.get_dig_p7()
            self.calibration.dig_p8 = CALIBRATION.get_dig_p8()
            self.calibration.dig_p9 = CALIBRATION.get_dig_p9()

            self.calibration.dig_h1 = CALIBRATION.get_dig_h1()

        with self._bme280.CALIBRATION2 as CALIBRATION:
            self.calibration.dig_h2 = CALIBRATION.get_dig_h2()
            self.calibration.dig_h3 = CALIBRATION.get_dig_h3()
            self.calibration.dig_h4 = CALIBRATION.get_dig_h4()
            self.calibration.dig_h5 = CALIBRATION.get_dig_h5()
            self.calibration.dig_h6 = CALIBRATION.get_dig_h6()

    def update_sensor(self):
        self.setup()

        if self._mode == "forced":
            # Trigger a reading in forced mode and wait for result
            self._bme280.CTRL_MEAS.set_mode("forced")
            while self._bme280.STATUS.get_measuring():
                time.sleep(0.001)

        with self._bme280.DATA as DATA:
            raw_temperature = DATA.get_temperature()
            raw_pressure = DATA.get_pressure()
            raw_humidity = DATA.get_humidity()

        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)
        return (self.temperature, self.pressure, self.humidity
                )  # TheHWcave: added return values

    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=1013.25):
        self.update_sensor()
        pressure = self.get_pressure()
        altitude = 44330.0 * (1.0 - pow(pressure / qnh, (1.0 / 5.255)))
        return altitude
Пример #6
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
Пример #7
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):
        if self._is_setup:
            return
        self._is_setup = True

        self._bmp280.select_address(self._i2c_addr)

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

        with self._bmp280.CTRL_MEAS as CTRL_MEAS:
            CTRL_MEAS.set_mode('normal')
            CTRL_MEAS.set_osrs_t(16)
            CTRL_MEAS.set_osrs_p(16)
            CTRL_MEAS.write()

        with self._bmp280.CONFIG as CONFIG:
            CONFIG.set_t_sb(500)
            CONFIG.set_filter(2)
            CONFIG.write()

        with self._bmp280.CALIBRATION as CALIBRATION:
            self.calibration.dig_t1 = CALIBRATION.get_dig_t1()
            self.calibration.dig_t2 = CALIBRATION.get_dig_t2()
            self.calibration.dig_t3 = CALIBRATION.get_dig_t3()

            self.calibration.dig_p1 = CALIBRATION.get_dig_p1()
            self.calibration.dig_p2 = CALIBRATION.get_dig_p2()
            self.calibration.dig_p3 = CALIBRATION.get_dig_p3()
            self.calibration.dig_p4 = CALIBRATION.get_dig_p4()
            self.calibration.dig_p5 = CALIBRATION.get_dig_p5()
            self.calibration.dig_p6 = CALIBRATION.get_dig_p6()
            self.calibration.dig_p7 = CALIBRATION.get_dig_p7()
            self.calibration.dig_p8 = CALIBRATION.get_dig_p8()
            self.calibration.dig_p9 = CALIBRATION.get_dig_p9()

    def update_sensor(self):
        self.setup()

        raw_temperature = self._bmp280.DATA.get_temperature()
        raw_pressure = self._bmp280.DATA.get_pressure()

        self.temperature = self.calibration.compensate_temperature(
            raw_temperature)
        self.pressure = self.calibration.compensate_pressure(raw_pressure)

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

    def get_pressure(self):
        self.update_sensor()
        return self.pressure
Пример #8
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)
Пример #9
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)
Пример #10
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
Пример #11
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