Source code for crappy.inout.mprls

# coding: utf-8

from time import time
from typing import Union
from .inout import InOut
from .._global import OptionalModule
from ..tool import ft232h_server as ft232h, Usb_server, i2c_msg_ft232h

try:
  import adafruit_mprls
except (ImportError, ModuleNotFoundError):
  adafruit_mprls = OptionalModule('adafruit_mprls')

try:
  import board
except (ImportError, ModuleNotFoundError):
  board = OptionalModule('board', 'Blinka is necessary to use the I2C bus')

try:
  from digitalio import DigitalInOut
except (ImportError, ModuleNotFoundError):
  DigitalInOut = OptionalModule('digitalio',
                                'Blinka is necessary to access the GPIOs')

try:
  from smbus2 import SMBus, i2c_msg
except (ImportError, ModuleNotFoundError):
  SMBus = i2c_msg = OptionalModule('smbus2')

try:
  import RPi.GPIO as GPIO
except (ModuleNotFoundError, ImportError):
  GPIO = OptionalModule("RPi.GPIO")

mprls_status_bits = {'busy': 0x20,
                     'memory error': 0x04,
                     'math saturation': 0x01}
mprls_backends = ['Pi4', 'blinka', 'ft232h']


[docs]class Mprls(Usb_server, InOut): """The Mprls inout is meant for reading pressure from Adafruit's Mprls pressure sensor. It communicates over I2C with the sensor. """
[docs] def __init__(self, backend: str, eoc_pin: Union[str, int] = None, device_address: int = 0x18, i2c_port: int = 1, ft232h_ser_num: str = None) -> None: """Initializes the parent class and opens the I2C bus. Args: backend (:obj:`str`): Should be one of : :: 'Pi4', 'blinka', 'ft232h' The `'Pi4'` backend is optimized but only works on boards supporting the :mod:`smbus2` module, like the Raspberry Pis. The `'blinka'` backend may be less performant and requires installing Adafruit's modules, but these modules are compatible with and maintained on a wide variety of boards. The `'ft232h'` backend allows controlling the MPRLS from a PC using Adafruit's FT232H USB to I2C adapter. See :ref:`Crappy for embedded hardware` for details. eoc_pin (:obj:`int` or :obj:`str`, optional): 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 bus, but requires one extra wire. With the backend `'Pi4'`, give the index of the GPIO in BCM convention. With the `'ft232h'` backend, give the name of the GPIO in the format `Dx` or `Cx`. With the backend `'blinka'`, it should be a string but the syntax varies according to the board. Refer to blinka's documentation for more information. device_address (:obj:`int`, optional): The I2C address of the MPRLS. The address of the devices sold by Adafruit is `0x18`, but other suppliers may sell it with another address. i2c_port (:obj:`int`, optional): The I2C port over which the MPRLS should communicate. On most Raspberry Pi models the default I2C port is `1`. ft232h_ser_num (:obj:`str`, optional): If backend is `'ft232h'`, the serial number of the FT232H to use for communication. """ if not isinstance(backend, str) or backend not in mprls_backends: raise ValueError("backend should be in {}".format(mprls_backends)) self._backend = backend Usb_server.__init__(self, serial_nr=ft232h_ser_num if ft232h_ser_num else '', backend=backend) queue, block_number, namespace, command_event, \ answer_event, next_event, done_event = super().start_server() InOut.__init__(self) if backend == 'ft232h': self._bus = ft232h(mode='I2C', queue=queue, namespace=namespace, command_event=command_event, answer_event=answer_event, block_number=block_number, next_block=next_event, done_event=done_event, serial_nr=ft232h_ser_num) if not isinstance(device_address, int): raise TypeError("device_address should be an integer.") self._address = device_address if not isinstance(i2c_port, int): raise TypeError("i2c_port should be an integer.") self._i2c_port = i2c_port if eoc_pin is not None: if backend == 'blinka' and not isinstance(eoc_pin, str): raise TypeError('eoc_pin should be a string when using the blinka ' 'backend !') elif backend == 'ft232h' and not isinstance(eoc_pin, str): raise TypeError('eoc_pin should be a string when using the ft232h ' 'backend !') elif backend == 'Pi4' and not isinstance(eoc_pin, int): raise TypeError('eoc_pin should be an int when using the Pi4 ' 'backend !') self._eoc_pin = eoc_pin
[docs] def open(self) -> None: """Opens the I2C bus.""" if self._backend == 'Pi4': self._bus = SMBus(self._i2c_port) if self._eoc_pin is not None: GPIO.setmode(GPIO.BCM) GPIO.setup(self._eoc_pin, GPIO.IN) elif self._backend == 'blinka': if self._eoc_pin is not None: eoc = DigitalInOut(getattr(board, self._eoc_pin)) else: eoc = None self._mpr = adafruit_mprls.MPRLS(board.I2C(), psi_min=0, psi_max=25, eoc_pin=eoc) self._i2c_msg = i2c_msg_ft232h if self._backend == 'ft232h' else i2c_msg
[docs] def get_data(self) -> list: """Reads the pressure value. Returns: The timestamp and the pressure value in hPa. """ if self._backend == 'blinka': pres = self._mpr.pressure else: # Starting conversion self._bus.i2c_rdwr(self._i2c_msg.write(self._address, [0xAA, 0x00, 0x00])) # Waiting for conversion to complete t0 = time() while not self._data_available(): if time() - t0 > 0.1: raise TimeoutError('Waited too long for data to be ready') # Reading conversion result read = self._i2c_msg.read(self._address, 4) self._bus.i2c_rdwr(read) out = list(read) # Checking if anu error occurred if out[0] & mprls_status_bits['memory error']: raise RuntimeError("A memory error occurred on the MPRLS") elif out[0] & mprls_status_bits['math saturation']: raise RuntimeError("A math saturation error occurred on the MPRLS") # Extracting conversion result as an integer ret = (out[1] << 16) | (out[2] << 8) | out[3] # Converting to hPa pres = 68.947572932 * (ret - 0x19999A) * 25 / (0xE66666 - 0x19999A) return [time(), pres]
[docs] def close(self): """Closes the I2C bus.""" if self._backend != 'blinka': self._bus.close() if self._backend == 'Pi4' and self._eoc_pin is not None: GPIO.cleanup()
def _data_available(self) -> bool: """Returns :obj:`True` if data is available, :obj:`False` otherwise.""" # EOC signal from the I2C communication if self._eoc_pin is None: wait = self._i2c_msg.read(self._address, 1) self._bus.i2c_rdwr(wait) return not list(wait)[0] & mprls_status_bits['busy'] # EOC signal from a GPIO elif self._backend == 'ft232h': return bool(self._bus.get_gpio(self._eoc_pin)) else: return bool(GPIO.input(self._eoc_pin))