Source code for crappy.inout.ft232h.ads1115

# coding: utf-8

from time import time
from typing import List, Optional
import logging

from ..meta_inout import InOut
from ...tool.ft232h import FT232HServer as FT232H, USBArgsType

# Register and other configuration values:
Ads1115_pointer_conversion = 0x00
Ads1115_pointer_config = 0x01
Ads1115_pointer_lo_thresh = 0x02
Ads1115_pointer_hi_thresh = 0x03

Ads1115_config_mux = {'A0 - A1': 0x0000,
                      'A0 - A3': 0x1000,
                      'A1 - A3': 0x2000,
                      'A2 - A3': 0x3000,
                      'A0': 0x4000,
                      'A1': 0x5000,
                      'A2': 0x6000,
                      'A3': 0x7000}

Ads1115_config_gain = {0.256: 0x0a00,
                       0.512: 0x0800,
                       1.024: 0x0600,
                       2.048: 0x0400,
                       4.096: 0x0200,
                       6.144: 0x0000}

Ads1115_config_dr = {8: 0x0000,
                     16: 0x0020,
                     32: 0x0040,
                     64: 0x0060,
                     128: 0x0080,
                     250: 0x00A0,
                     475: 0x00C0,
                     860: 0x00E0}


[docs] class ADS1115FT232H(InOut): """A class for controlling Adafruit's ADS1115 16-bits ADC through an FT232H. It is similar to the :class:`~crappy.inout.ADS1115` class, except this class is specific for use with an :class:`~crappy.tool.ft232h.FT232H` USB to I2C converter. The ADS1115 InOut is meant for reading conversion values from a 16-bits ADS1115 ADC, using the I2C protocol. The output is in Volts by default, but a ``gain`` and an ``offset`` can be specified. Various settings can be adjusted, like the sample rate, the input mode or the voltage range. .. versionadded:: 2.0.0 """ ft232h = True
[docs] def __init__(self, device_address: int = 0x48, sample_rate: int = 128, v_range: float = 2.048, multiplexer: str = 'A1', dry_pin: Optional[str] = None, gain: float = 1, offset: float = 0, _ft232h_args: USBArgsType = tuple()) -> None: """Checks the validity of the arguments. Args: device_address: The I2C address of the ADS1115. The default address is `0x48`, but it is possible to change this setting using the `ADDR` pin. sample_rate: The sample rate for data conversion (in SPS). The available sample rates are: :: 8, 16, 32, 64, 128, 250, 475, 860 v_range: The value (in Volts) of the measured signal corresponding to the `0x7FFF` output in bits, i.e. that saturates the sensor. A signal of ``-v_range`` Volts gives a `0x8000` output in bits. Available ``v_range`` values are: :: 0.256, 0.512, 1.024, 2.048, 4.096, 6.144 multiplexer: Choice of the inputs to consider. Single-input modes actually measure `Ax - GND`. The available ``multiplexer`` values are: :: 'A0', 'A1', 'A2', 'A3', 'A0 - A1', 'A0 - A3', 'A1 - A3', 'A2 - A3' dry_pin: Optionally, reads the end of conversion signal from a GPIO rather than from an I2C message. Speeds up the reading and decreases the traffic on the I2C bus, but requires one extra wire. Give the name of the GPIO in the format `Dx` or `Cx`. gain: Allows to tune the output value according to the formula : :math:`output = gain * tension + offset`. offset: Allows to tune the output value according to the formula : :math:`output = gain * tension + offset`. _ft232h_args: This argument is meant for internal use only and should not be provided by the user. It contains the information necessary for setting up the FT232H. Warning: AINx voltages should not be higher than `VDD+0.3V` nor lower than `GND-0.3V`. Setting high ``v_range`` values does not allow measuring voltages higher than `VDD` !! """ self._bus = None super().__init__() (block_index, block_lock, command_file, answer_file, shared_lock, current_block) = _ft232h_args self._bus = FT232H(mode='I2C', block_index=block_index, current_block=current_block, command_file=command_file, answer_file=answer_file, block_lock=block_lock, shared_lock=shared_lock) self._device_address = device_address if v_range not in Ads1115_config_gain: raise ValueError("v_range should be in {}".format(list( Ads1115_config_gain.keys()))) else: self._v_range = v_range if sample_rate not in Ads1115_config_dr: raise ValueError("sample_rate should be in {}".format(list( Ads1115_config_dr.keys()))) else: self._sample_rate = sample_rate if multiplexer not in Ads1115_config_mux: raise ValueError("multiplexer should be in {}".format(list( Ads1115_config_mux.keys()))) else: self._multiplexer = multiplexer if dry_pin is not None and not isinstance(dry_pin, str): raise TypeError('int_pin should be a string !') self._dry_pin = dry_pin self._gain = gain self._offset = offset
[docs] def open(self) -> None: """Initializes the I2C communication and the device.""" if not self._is_connected(): raise IOError("The ADS1115 is not connected") # Setting the configuration register according to the user values init_value = Ads1115_config_mux[self._multiplexer] init_value |= Ads1115_config_gain[self._v_range] init_value |= 0x0100 # Single shot operating mode init_value |= Ads1115_config_dr[self._sample_rate] self.log(logging.INFO, "Initializing the ADS1115") if self._dry_pin is None: # Setting the two last bits to 11 to disable the DRY pin init_value |= 0x0003 self._set_register(Ads1115_pointer_config, init_value) # Setting the threshold registers to activate the DRY pin output if self._dry_pin is not None: self._set_register(Ads1115_pointer_lo_thresh, 0x0000) self._set_register(Ads1115_pointer_hi_thresh, 0xFFFF)
[docs] def get_data(self) -> List[float]: """Reads the registers containing the conversion result. The output is in Volts, unless a gain and offset are applied. Returns: A :obj:`list` containing the timestamp and the voltage value. """ # Reading the config register, and setting it so that the ADS1115 starts # a conversion ms_byte, ls_byte = self._bus.read_i2c_block_data(self._device_address, Ads1115_pointer_config, 2) self._set_register(Ads1115_pointer_config, ((ms_byte | 0x80) << 8) | ls_byte) # Waiting for the end of the conversion t0 = time() while not self._data_available(): if time() - t0 > 0.5: raise TimeoutError('Waited too long for data to be ready !') # Reading the output of the conversion out = [time()] ms_byte, ls_byte = self._bus.read_i2c_block_data( self._device_address, Ads1115_pointer_conversion, 2) self.log(logging.DEBUG, f"Read {ms_byte, ls_byte} from the device " f"address {self._device_address} at " f"register {Ads1115_pointer_conversion}") # Converting the output value into Volts value_raw = (ms_byte << 8) | ls_byte if ms_byte >> 7: value_raw -= 2 ** 16 value = value_raw * self._v_range / 2 ** 15 out.append(self._offset + self._gain * value) return out
[docs] def close(self) -> None: """Closes the I2C bus.""" if self._bus is not None: self.log(logging.INFO, "Closing the ADS1115") self._bus.close()
def _set_register(self, register_address: int, value: int) -> None: """Thin wrapper for writing data to the registers.""" self.log(logging.DEBUG, f"Writing {[(value >> 8) & 0xFF, value & 0xFF]} " f"to the address {self._device_address} in " f"register {register_address}") self._bus.write_i2c_block_data(self._device_address, register_address, [(value >> 8) & 0xFF, value & 0xFF]) def _is_connected(self) -> bool: """Tries reading a byte from the device. Returns: :obj:`True` if reading was successful, else :obj:`False`. """ try: self._bus.read_byte(self._device_address) return True except IOError: return False def _data_available(self) -> bool: """Returns :obj:`True` if data is available, :obj:`False` otherwise.""" # EOC signal from the I2C communication if self._dry_pin is None: return bool(self._bus.read_i2c_block_data(self._device_address, Ads1115_pointer_config, 1)[0] & 0x80) # EOC signal from a GPIO else: return not bool(self._bus.get_gpio(self._dry_pin))