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()
        altitude = 44330.0 * (1.0 - pow(pressure / qnh, (1.0 / 5.255)))
        return altitude

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

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

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

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

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

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

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

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

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

        Accepts up to 8 arguments of type PlayWaveform or WaitMillis.

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

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

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

    def busy(self):
        """Check if DRV2605 is busy."""
        return self._drv2605.get('GO').go
Example #3
0
class LTR559:
    def __init__(self,
                 i2c_dev=None,
                 enable_interrupts=False,
                 interrupt_pin_polarity=1,
                 timeout=5.0):
        """Initialise the LTR559.

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

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

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

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

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

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

        self._ltr559 = Device(
            I2C_ADDR,
            i2c_dev=i2c_dev,
            bit_width=8,
            registers=(
                Register('ALS_CONTROL',
                         0x80,
                         fields=(BitField('gain',
                                          0b00011100,
                                          adapter=LookupAdapter({
                                              1: 0b000,
                                              2: 0b001,
                                              4: 0b010,
                                              8: 0b011,
                                              48: 0b110,
                                              96: 0b111
                                          })),
                                 BitField('sw_reset', 0b00000010),
                                 BitField('mode', 0b00000001))),
                Register('PS_CONTROL',
                         0x81,
                         fields=(BitField('saturation_indicator_enable',
                                          0b00100000),
                                 BitField('active',
                                          0b00000011,
                                          adapter=LookupAdapter({
                                              False: 0b00,
                                              True: 0b11
                                          })))),
                Register('PS_LED',
                         0x82,
                         fields=(BitField('pulse_freq_khz',
                                          0b11100000,
                                          adapter=LookupAdapter({
                                              30: 0b000,
                                              40: 0b001,
                                              50: 0b010,
                                              60: 0b011,
                                              70: 0b100,
                                              80: 0b101,
                                              90: 0b110,
                                              100: 0b111
                                          })),
                                 BitField('duty_cycle',
                                          0b00011000,
                                          adapter=LookupAdapter({
                                              0.25: 0b00,
                                              0.5: 0b01,
                                              0.75: 0b10,
                                              1.0: 0b11
                                          })),
                                 BitField('current_ma',
                                          0b00000111,
                                          adapter=LookupAdapter({
                                              5: 0b000,
                                              10: 0b001,
                                              20: 0b010,
                                              50: 0b011,
                                              100: 0b100
                                          })))),
                Register('PS_N_PULSES',
                         0x83,
                         fields=(BitField('count', 0b00001111), )),
                Register('PS_MEAS_RATE',
                         0x84,
                         fields=(BitField('rate_ms',
                                          0b00001111,
                                          adapter=LookupAdapter({
                                              10: 0b1000,
                                              50: 0b0000,
                                              70: 0b0001,
                                              100: 0b0010,
                                              200: 0b0011,
                                              500: 0b0100,
                                              1000: 0b0101,
                                              2000: 0b0110
                                          })), )),
                Register('ALS_MEAS_RATE',
                         0x85,
                         fields=(BitField('integration_time_ms',
                                          0b00111000,
                                          adapter=LookupAdapter({
                                              100: 0b000,
                                              50: 0b001,
                                              200: 0b010,
                                              400: 0b011,
                                              150: 0b100,
                                              250: 0b101,
                                              300: 0b110,
                                              350: 0b111
                                          })),
                                 BitField('repeat_rate_ms',
                                          0b00000111,
                                          adapter=LookupAdapter({
                                              50: 0b000,
                                              100: 0b001,
                                              200: 0b010,
                                              500: 0b011,
                                              1000: 0b100,
                                              2000: 0b101
                                          })))),
                Register(
                    'PART_ID',
                    0x86,
                    fields=(
                        BitField('part_number', 0b11110000),  # Should be 0x09H
                        BitField('revision', 0b00001111)  # Should be 0x02H
                    ),
                    read_only=True,
                    volatile=False),
                Register(
                    'MANUFACTURER_ID',
                    0x87,
                    fields=(
                        BitField('manufacturer_id',
                                 0b11111111),  # Should be 0x05H
                    ),
                    read_only=True),

                # This will address 0x88, 0x89, 0x8A and 0x8B as a continuous 32bit register
                Register('ALS_DATA',
                         0x88,
                         fields=(BitField('ch1',
                                          0xFFFF0000,
                                          bit_width=16,
                                          adapter=U16ByteSwapAdapter()),
                                 BitField('ch0',
                                          0x0000FFFF,
                                          bit_width=16,
                                          adapter=U16ByteSwapAdapter())),
                         read_only=True,
                         bit_width=32),
                Register(
                    'ALS_PS_STATUS',
                    0x8C,
                    fields=(
                        BitField('als_data_valid', 0b10000000),
                        BitField('als_gain',
                                 0b01110000,
                                 adapter=LookupAdapter({
                                     1: 0b000,
                                     2: 0b001,
                                     4: 0b010,
                                     8: 0b011,
                                     48: 0b110,
                                     96: 0b111
                                 })),
                        BitField('als_interrupt',
                                 0b00001000),  # True = Interrupt is active
                        BitField('als_data',
                                 0b00000100),  # True = New data available
                        BitField('ps_interrupt',
                                 0b00000010),  # True = Interrupt is active
                        BitField('ps_data',
                                 0b00000001)  # True = New data available
                    ),
                    read_only=True),

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

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

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

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

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

                # In the case of both PS and ALS, a 0 value indicates that every value outside
                # the threshold range should be counted.
                # Values therein map to n+1 , ie: 0b0001 requires two consecutive values.
                Register('INTERRUPT_PERSIST',
                         0x9E,
                         fields=(BitField('PS', 0xF0), BitField('ALS',
                                                                0x0F)))))
        """Set up the LTR559 sensor"""
        self.part_id = self._ltr559.get('PART_ID')
        if self.part_id.part_number != PART_ID or self.part_id.revision != REVISION_ID:
            raise RuntimeError("LTR559 not found")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        Returns the Part Number ID portion of the PART_ID register.

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

        """
        return self.part_id.part_number

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

        Returns the Revision ID portion of the PART_ID register.

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

        """
        return self.part_id.revision

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

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

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

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

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

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

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

        Set the upper and lower threshold for the proximity interrupt.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        """
        mode = []

        if enable_light:
            mode.append('als')

        if enable_proximity:
            mode.append('ps')

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

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

        :param active: True for enabled, False for disabled

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

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

        :param enabled: True for enabled, False for disabled

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

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

        :param offset: Offset value from 0 to 1023

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        """
        return self._integration_time

    get_intt = get_integration_time

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

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

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

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

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

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

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

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

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

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

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

        Returns the raw proximity reading from the sensor.

        A closer object produces a larger value.

        """
        if not passive:
            self.update_sensor()
        return self._ps0
Example #4
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)
Example #5
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=1013.25):
        self.update_sensor()
        pressure = self.get_pressure()
        altitude = 44330.0 * (1.0 - pow(pressure / qnh, (1.0 / 5.255)))
        return altitude
Example #6
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
Example #7
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 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)
Example #8
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
Example #9
0
class TCS3472:
    def __init__(self, i2c_dev=None):
        """Initialise the TCS3472."""

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

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

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

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

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

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

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

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

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

        scale = max(rgbc)

        try:
            return self._rgbc_tuple(
                time.time(),
                int((float(rgbc.red) / scale) * 255),
                int((float(rgbc.green) / scale) * 255),
                int((float(rgbc.blue) / scale) * 255),
                float(rgbc.red) / self._max_count,
                float(rgbc.green) / self._max_count,
                float(rgbc.blue) / self._max_count,
                float(rgbc.clear) / self._max_count
            )
        except ZeroDivisionError:
            return self._rgbc_tuple(
                time.time(),
                0,
                0,
                0,
                float(rgbc.red) / self._max_count,
                float(rgbc.green) / self._max_count,
                float(rgbc.blue) / self._max_count,
                float(rgbc.clear) / self._max_count
            )
Example #10
0
class INA220:
    def __init__(self, i2c_addr=0x45, i2c_dev=None):
        self._i2c_addr = i2c_addr
        self._i2c_dev = i2c_dev
        self._is_setup = False

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

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

            )),

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

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

            ), bit_width=16, read_only=True),

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

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

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

        self._configuration = self.get_configuration()

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

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

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

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

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

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

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

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

        return current_draw, bus_voltage, shunt_voltage