Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iteration support for touchpads #113

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 138 additions & 21 deletions adafruit_circuitplayground/circuit_playground_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,95 @@ def light(self):
return self._photocell.value * 330 // (2 ** 16)


class InterableInput:
"""Wrapper class for iterable touchpad inputs

:param name_list: A list of pin names to initialize as touchpad inputs
"""

def __init__(self, name_list):
self._input_names = name_list
input_pins = [getattr(board, name) for name in name_list]
self._inputs = list(input_pins)
self._current_input = 0
self._len_inputs = len(name_list)

def __iter__(self):
for input_tio in self._inputs:
yield self._auto_convert_tio(input_tio)

def __getitem__(self, index):
input_name = self._use_str_name(index)
for name, tio in zip(self._input_names, self._inputs):
if name == input_name:
return self._auto_convert_tio(tio)
raise ValueError(
"The given padname is either not a valid touchpad or was deinitialized"
)

def _auto_convert_tio(self, input_pad):
"""Automagically turns an existing pin into a touchio.TouchIn as needed, and
also returns it
"""

if not isinstance(input_pad, touchio.TouchIn):
name_index = self._inputs.index(input_pad)
self._inputs[name_index] = touchio.TouchIn(self._inputs[name_index])
input_pad = self._inputs[name_index]
return input_pad

@staticmethod
def _use_str_name(value):
"""Converts an index into the pin name if needed"""

if value in (7, "A7", "TX"):
return "TX"
if isinstance(value, int):
if value not in range(1, 7):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is no longer needed given my general comment, but note that range(1, 7) will allocate storage. Also, the upper limit is exclusive and is not included; it will only go from 1 to 6.

Suggested change
if value not in range(1, 7):
if not (1 <= value <= 7):

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh neat, I didn't think about that, thanks!

raise ValueError("Pins available as touchpads are 1-7")
return "A" + str(value)
if isinstance(value, str):
if not value.startswith("A") and int(value[1:]) not in range(1, 7):
raise ValueError("Pins available as touchpads are A1-A6 and A7/TX")
return value
raise TypeError(
"Iterable inputs can only be accessed by int index or analog string names"
)

def deinit_input(self, input_name):
"""Deinitialize a given pin as a touchpad input, freeing up the memory and allowing the
pin to be used as a different type of input

:param input_name: The name or pad number to be deinitialized
:type input_name: str|int
"""

input_name = self._use_str_name(input_name)
if input_name in self._input_names:
input_index = self._input_names.index(input_name)
self._input_names.pop(input_index)
selected_tio = self._inputs.pop(input_index)
if isinstance(selected_tio, touchio.TouchIn):
selected_tio.deinit()

def init_input(self, input_name):
"""Initializes a given pin as a touchpad input, if not already

:param input_name: The name or pad number to be initialized
:type input_name: str|int
"""

input_name = self._use_str_name(input_name)
if input_name not in self._input_names:
self._inputs.append(getattr(board, input_name))
self._input_names.append(input_name)

@property
def names(self):
"""Returns the names of all pins currently set up as touchpad inputs"""
return self._input_names


class CircuitPlaygroundBase: # pylint: disable=too-many-public-methods
"""Circuit Playground base class."""

Expand Down Expand Up @@ -77,22 +166,21 @@ def __init__(self):
self._light = Photocell(board.LIGHT)

# Define touch:
# Initially, self._touches stores the pin used for a particular touch. When that touch is
# used for the first time, the pin is replaced with the corresponding TouchIn object.
# This saves a little RAM over using a separate read-only pin tuple.
# For example, after `cp.touch_A2`, self._touches is equivalent to:
# [None, board.A1, touchio.TouchIn(board.A2), board.A3, ...]
# Initially, IterableInput stores the pin used for a particular touch. When that touch is
# used for the first time, the stored pin is replaced with the corresponding TouchIn object.
# This saves a little RAM over initializing all pins as inputs immediately.
# Slot 0 is not used (A0 is not allowed as a touch pin).
self._touches = [
None,
board.A1,
board.A2,
board.A3,
board.A4,
board.A5,
board.A6,
board.TX,
]
self._touches = InterableInput(
[
"A1",
"A2",
"A3",
"A4",
"A5",
"A6",
"TX",
]
)
self._touch_threshold_adjustment = 0

# Define acceleration:
Expand Down Expand Up @@ -138,7 +226,7 @@ def detect_taps(self):
@staticmethod
def _default_tap_threshold(tap):
if (
"nRF52840" in os.uname().machine
"nRF52840" in os.uname().machine # pylint: disable=no-member
): # If we're on a CPB, use a higher tap threshold
return 100 if tap == 1 else 70

Expand Down Expand Up @@ -351,13 +439,42 @@ def shake(self, shake_threshold=30):
return self._lis3dh.shake(shake_threshold=shake_threshold)

def _touch(self, i):
if not isinstance(self._touches[i], touchio.TouchIn):
# First time referenced. Get the pin from the slot for this touch
# and replace it with a TouchIn object for the pin.
self._touches[i] = touchio.TouchIn(self._touches[i])
self._touches[i].threshold += self._touch_threshold_adjustment
return self._touches[i].value

def deinit_touchpad(self, touchpad_pin):
"""Deinitializes an input as a touchpad to free it up for use elsewhere

:param touchpad_pin: The touchpad name or number to deinitialize
:type touchpad_pin: str|int
"""

self._touches.deinit_input(touchpad_pin)

def init_touchpad(self, touchpad_pin):
"""Initializes a pin as a touchpad input

:param touchpad_pin: The touchpad name or number to initialize
:type touchpad_pin: str|int
"""

self._touches.init_input(touchpad_pin)

@property
def touchpads(self):
"""A list of all touchpad names currently set up as touchpad inputs"""

return self._touches.names

@property
def touched(self):
"""A list of touchpad input names currently registering as being touched"""

return [
touch_name
for touch_pad, touch_name in zip(self._touches, self._touches.names)
if touch_pad.value
]

# We chose these verbose touch_A# names so that beginners could use it without understanding
# lists and the capital A to match the pin name. The capitalization is not strictly Python
# style, so everywhere we use these names, we whitelist the errors using:
Expand Down
22 changes: 8 additions & 14 deletions examples/circuitplayground_touch_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@
"""This example prints to the serial console when you touch the capacitive touch pads."""
from adafruit_circuitplayground import cp

print("Here are all the possible touchpads:")
print(cp.touchpads)

while True:
if cp.touch_A1:
print("Touched pad A1")
if cp.touch_A2:
print("Touched pad A2")
if cp.touch_A3:
print("Touched pad A3")
if cp.touch_A4:
print("Touched pad A4")
if cp.touch_A5:
print("Touched pad A5")
if cp.touch_A6:
print("Touched pad A6")
if cp.touch_TX:
print("Touched pad TX")
print("Touchpads currently registering a touch:")
print(cp.touched)

if all(pad in cp.touched for pad in ("A2", "A3", "A4")):
print("This only prints when A2, A3, and A4 are being held at the same time!")
17 changes: 17 additions & 0 deletions examples/circuitplayground_touchpad_changing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2021 Alec Delaney
# SPDX-License-Identifier: MIT

"""This example prints to the serial console when you touch the capacitive touch pads."""
from adafruit_circuitplayground import cp

print("Here are all the initially registered touchpads:")
print(cp.touchpads)

print("You can remove a few if you need those pins:")
cp.deinit_touchpad("A2")
cp.deinit_touchpad("A5")
print(cp.touchpads)

print("You can also readd them later!")
cp.init_touchpad("A2")
print(cp.touchpads)