Skip to content

Commit

Permalink
Merge pull request #34 from JulianOrteil/fix-32
Browse files Browse the repository at this point in the history
Fix #32
  • Loading branch information
FoamyGuy authored Jun 18, 2022
2 parents 011a184 + 915572c commit 91a5b7b
Showing 1 changed file with 104 additions and 36 deletions.
140 changes: 104 additions & 36 deletions adafruit_mpr121.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@

import time

try:
from typing import List, Optional, Tuple
import busio
except ImportError:
# typing hint modules not needed or not available in CircuitPython
pass


from adafruit_bus_device import i2c_device
from micropython import const

Expand Down Expand Up @@ -79,71 +87,96 @@

class MPR121_Channel:
# pylint: disable=protected-access
"""Helper class to represent a touch channel on the MPR121. Not meant to
be used directly."""
"""Represents a single channel on the touch sensor.
Not meant to be used directly.
"""

_mpr121: "MPR121"
_channel: int

def __init__(self, mpr121, channel):
def __init__(self, mpr121: "MPR121", channel: int) -> None:
"""Creates a new ``MPR121_Channel`` instance.
:param mpr121: An instance of the touch sensor driver.
:param channel: The channel this instance represents (0-11).
"""
self._mpr121 = mpr121
self._channel = channel

@property
def value(self):
"""Whether the touch pad is being touched or not."""
def value(self) -> bool:
"""Get whether the touch pad is being touched or not."""
return self._mpr121.touched() & (1 << self._channel) != 0

@property
def raw_value(self):
"""The raw touch measurement."""
def raw_value(self) -> int:
"""Get the raw touch measurement."""
return self._mpr121.filtered_data(self._channel)

@property
def threshold(self):
"""The touch threshold."""
def threshold(self) -> int:
"""Get or set the touch threshold."""
buf = bytearray(1)
self._mpr121._read_register_bytes(MPR121_TOUCHTH_0 + 2 * self._channel, buf, 1)
return buf[0]

@threshold.setter
def threshold(self, value):
self._mpr121._write_register_byte(MPR121_TOUCHTH_0 + 2 * self._channel, value)
def threshold(self, new_thresh: int) -> None:
self._mpr121._write_register_byte(
MPR121_TOUCHTH_0 + 2 * self._channel, new_thresh
)

@property
def release_threshold(self):
"""The release threshold."""
def release_threshold(self) -> int:
"""Get or set the release threshold."""
buf = bytearray(1)
self._mpr121._read_register_bytes(
MPR121_RELEASETH_0 + 2 * self._channel, buf, 1
)
return buf[0]

@release_threshold.setter
def release_threshold(self, value):
self._mpr121._write_register_byte(MPR121_RELEASETH_0 + 2 * self._channel, value)
def release_threshold(self, new_thresh: int) -> None:
self._mpr121._write_register_byte(
MPR121_RELEASETH_0 + 2 * self._channel, new_thresh
)


class MPR121:
"""Driver for the MPR121 capacitive touch breakout board."""

def __init__(self, i2c, address=MPR121_I2CADDR_DEFAULT):
_i2c: i2c_device.I2CDevice
_buffer: bytearray
_channels: List[Optional[MPR121_Channel]]

def __init__(self, i2c: busio.I2C, address: int = MPR121_I2CADDR_DEFAULT) -> None:
"""Creates a new ``MPR121`` instance.
:param i2c: An I2C driver.
:type i2c: class:`busio.I2C`
:param address: The address of the touch sensor (0x5A - 0x5D).
:type address: int
"""
self._i2c = i2c_device.I2CDevice(i2c, address)
self._buffer = bytearray(2)
self._channels = [None] * 12
self.reset()

def __getitem__(self, key):
def __getitem__(self, key: int) -> MPR121_Channel:
if key < 0 or key > 11:
raise IndexError("Pin must be a value 0-11.")
raise IndexError("pin must be a value 0-11")
if self._channels[key] is None:
self._channels[key] = MPR121_Channel(self, key)
return self._channels[key]

@property
def touched_pins(self):
"""A tuple of touched state for all pins."""
def touched_pins(self) -> Tuple[bool]:
"""Get a tuple of the touched state for all pins."""
touched = self.touched()
return tuple(bool(touched >> i & 1) for i in range(12))

def _write_register_byte(self, register, value):
def _write_register_byte(self, register: int, value: int) -> None:
# Write a byte value to the specifier register address.
# MPR121 must be put in Stop Mode to write to most registers
stop_required = True
Expand All @@ -156,7 +189,9 @@ def _write_register_byte(self, register, value):
if stop_required:
self._i2c.write(bytes([MPR121_ECR, 0x8F]))

def _read_register_bytes(self, register, result, length=None):
def _read_register_bytes(
self, register: int, result: bytearray, length: Optional[int] = None
) -> None:
# Read the specified register address and fill the specified result byte
# array with result bytes. Make sure result buffer is the desired size
# of data to read.
Expand All @@ -165,23 +200,32 @@ def _read_register_bytes(self, register, result, length=None):
with self._i2c:
self._i2c.write_then_readinto(bytes([register]), result, in_end=length)

def reset(self):
"""Reset the MPR121 into a default state ready to detect touch inputs."""
def reset(self) -> None:
"""Reset the MPR121 into a default state.
All configurations and states previously set are lost.
:raises RuntimeError: The sensor is in an invalid config state.
"""
# Write to the reset register.
self._write_register_byte(MPR121_SOFTRESET, 0x63)
time.sleep(
0.001
) # This 1ms delay here probably isn't necessary but can't hurt.

# Set electrode configuration to default values.
self._write_register_byte(MPR121_ECR, 0x00)

# Check CDT, SFI, ESI configuration is at default values.
self._read_register_bytes(MPR121_CONFIG2, self._buffer, 1)
if self._buffer[0] != 0x24:
raise RuntimeError("Failed to find MPR121 in expected config state!")

# Default touch and release thresholds
for i in range(12):
self._write_register_byte(MPR121_TOUCHTH_0 + 2 * i, 12)
self._write_register_byte(MPR121_RELEASETH_0 + 2 * i, 6)

# Configure baseline filtering control registers.
self._write_register_byte(MPR121_MHDR, 0x01)
self._write_register_byte(MPR121_NHDR, 0x01)
Expand All @@ -194,43 +238,67 @@ def reset(self):
self._write_register_byte(MPR121_NHDT, 0x00)
self._write_register_byte(MPR121_NCLT, 0x00)
self._write_register_byte(MPR121_FDLT, 0x00)

# Set other configuration registers.
self._write_register_byte(MPR121_DEBOUNCE, 0)
self._write_register_byte(MPR121_CONFIG1, 0x10) # default, 16uA charge current
self._write_register_byte(MPR121_CONFIG2, 0x20) # 0.5uS encoding, 1ms period

# Enable all electrodes.
self._write_register_byte(
MPR121_ECR, 0x8F
) # start with first 5 bits of baseline tracking

def filtered_data(self, pin):
"""Return filtered data register value for the provided pin (0-11).
Useful for debugging.
def filtered_data(self, pin: int) -> int:
"""Get the filtered data register value.
:param pin: The pin to read (0 - 11).
:type pin: int
:raises ValueError: Argument ``pin`` is invalid.
:return: The filtered data value stored in the register.
:rtype: int
"""
if pin < 0 or pin > 11:
raise ValueError("Pin must be a value 0-11.")
self._read_register_bytes(MPR121_FILTDATA_0L + pin * 2, self._buffer)
return ((self._buffer[1] << 8) | (self._buffer[0])) & 0xFFFF

def baseline_data(self, pin):
"""Return baseline data register value for the provided pin (0-11).
Useful for debugging.
def baseline_data(self, pin: int) -> int:
"""Get the baseline data register value.
:param pin: The pin to read (0 - 11).
:type pin: int
:raises ValueError: Argument ``pin`` is invalid.
:return: The baseline data value stored in the register.
:rtype: int
"""
if pin < 0 or pin > 11:
raise ValueError("Pin must be a value 0-11.")
self._read_register_bytes(MPR121_BASELINE_0 + pin, self._buffer, 1)
return self._buffer[0] << 2

def touched(self):
"""Return touch state of all pins as a 12-bit value where each bit
represents a pin, with a value of 1 being touched and 0 not being touched.
def touched(self) -> int:
"""Get the touch state of all pins as a 12-bit value.
:return: A 12-bit value representing the touch state of each
pin. Each state in the value is represented by either a 1 or
0; touched or not.
:rtype: int
"""
self._read_register_bytes(MPR121_TOUCHSTATUS_L, self._buffer)
return ((self._buffer[1] << 8) | (self._buffer[0])) & 0xFFFF

def is_touched(self, pin):
"""Return True if the specified pin is being touched, otherwise returns
False.
def is_touched(self, pin: int) -> bool:
"""Get if ``pin`` is being touched.
:raises ValueError: Argument ``pin`` is invalid.
:return: True if ``pin`` is being touched; otherwise False.
:rtype: bool
"""
if pin < 0 or pin > 11:
raise ValueError("Pin must be a value 0-11.")
Expand Down

0 comments on commit 91a5b7b

Please sign in to comment.