Your microcontroller board has capacitive touch capabilities on multiple pins. The CircuitPython touchio module makes it simple to detect when you touch a pin, enabling you to use it as an input.

This section first covers using the touchio module to read touches on one pin. You'll learn how to setup the pin in your program, and read the touch status. Then, you'll learn how to read touches on multiple pins in a single example. Time to get started!

One Capacitive Touch Pin

The first example covered here will show you how to read touches on one pin.

Pin Wiring

Capacitive touch always benefits from the use of a 1MΩ pulldown resistor. Some microcontrollers have pulldown resistors built in, but using the built-in ones can yield unexpected results. Other microcontrollers to not have built-in pulldowns, and require an external pulldown resistor. Therefore, the best option is to include one regardless.

  • One leg of 1MΩ resistor to Metro A0
  • Opposite leg of 1MΩ resistor to Metro GND

Reading Touch on the Pin

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython Capacitive Touch Pin Example - Print to the serial console when one pin is touched.
"""
import time
import board
import touchio

touch = touchio.TouchIn(board.A0)

while True:
    if touch.value:
        print("Pin touched!")
    time.sleep(0.1)
Touch is designed to calibrate on board startup. In some cases, the ESP32-S3 does not calibrate properly, and the board running this code will report the pin as "touched" when you are not touching it. You can update the threshold manually in these cases to resolve this issue. See the end of this page for details.

Now touch the pin indicated in the diagram above. You'll see Pin touched! printed to the serial console!

First you import three modules: time, board and touchio. This makes these modules available for use in your code. All three are built-in to CircuitPython, so you don't find any library files in the Project Bundle.

Next, you create the touchio.TouchIn() object, and provide it the pin name using the board module. You save that to the touch variable.

Inside the loop, you check to see if the pin is touched. If so, you print to the serial console. Finally, you include a time.sleep() to slow it down a bit so the output is readable.

That's all there is to reading touch on a single pin using touchio in CircuitPython!

Multiple Capacitive Touch Pins

The next example shows you how to read touches on multiple pins in a single program.

Pin Wiring

  • One leg of first 1MΩ resistor to Metro A0
  • Opposite leg of first 1MΩ resistor to Metro GND
  • One leg of second 1MΩ resistor to Metro A5
  • Opposite leg of second 1MΩ resistor to Metro GND

Reading Touch on the Pins

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython Capacitive Two Touch Pin Example - Print to the serial console when a pin is touched.
"""
import time
import board
import touchio

touch_one = touchio.TouchIn(board.A0)
touch_two = touchio.TouchIn(board.A5)

while True:
    if touch_one.value:
        print("Pin one touched!")
    if touch_two.value:
        print("Pin two touched!")
    time.sleep(0.1)
Touch is designed to calibrate on board startup. In some cases, the ESP32-S3 does not calibrate properly, and the board running this code will report the pins as "touched" when you are not touching them. You can update the threshold manually in these cases to resolve this issue. See the end of this page for details.

Touch the pins to see the messages printed to the serial console!

This example builds on the first. The imports remain the same.

The touchio.TouchIn() object is created, but is instead saved to touch_one. A second touchio.TouchIn() object is also created, the second pin is provided to it using the board module, and is saved to touch_two.

Inside the loop, we check to see if pin one and pin two are touched, and if so, print to the serial console Pin one touched! and Pin two touched!, respectively. The same time.sleep() is included.

If more touch-capable pins are available on your board, you can easily add them by expanding on this example!

Where are my Touch-Capable pins?

There are specific pins on a microcontroller that support capacitive touch. How do you know which pins will work? Easy! Run the script below to get a list of all the pins that are available.

Save the following to your CIRCUITPY drive as code.py.

Click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, find your CircuitPython version, and copy the matching code.py file to your CIRCUITPY drive.

Your CIRCUITPY drive should now look similar to the following image:

CIRCUITPY
# SPDX-FileCopyrightText: 2021-2023 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython Touch-Compatible Pin Identification Script

Depending on the order of the pins in the CircuitPython pin definition, some inaccessible pins
may be returned in the script results. Consult the board schematic and use your best judgement.

In some cases, such as LED, the associated pin, such as D13, may be accessible. The LED pin
name is first in the list in the pin definition, and is therefore printed in the results. The
pin name "LED" will work in code, but "D13" may be more obvious. Use the schematic to verify.
"""
import board
import touchio
from microcontroller import Pin


def get_pin_names():
    """
    Gets all unique pin names available in the board module, excluding a defined list.
    This list is not exhaustive, and depending on the order of the pins in the CircuitPython
    pin definition, some of the pins in the list may still show up in the script results.
    """
    exclude = [
        "NEOPIXEL",
        "APA102_MOSI",
        "APA102_SCK",
        "LED",
        "NEOPIXEL_POWER",
        "BUTTON",
        "BUTTON_UP",
        "BUTTON_DOWN",
        "BUTTON_SELECT",
        "DOTSTAR_CLOCK",
        "DOTSTAR_DATA",
        "IR_PROXIMITY",
        "SPEAKER_ENABLE",
        "BUTTON_A",
        "BUTTON_B",
        "POWER_SWITCH",
        "SLIDE_SWITCH",
        "TEMPERATURE",
        "ACCELEROMETER_INTERRUPT",
        "ACCELEROMETER_SDA",
        "ACCELEROMETER_SCL",
        "MICROPHONE_CLOCK",
        "MICROPHONE_DATA",
        "RFM_RST",
        "RFM_CS",
        "RFM_IO0",
        "RFM_IO1",
        "RFM_IO2",
        "RFM_IO3",
        "RFM_IO4",
        "RFM_IO5",
    ]
    pins = [
        pin
        for pin in [getattr(board, p) for p in dir(board) if p not in exclude]
        if isinstance(pin, Pin)
    ]
    pin_names = []
    for pin_object in pins:
        if pin_object not in pin_names:
            pin_names.append(pin_object)
    return pin_names


for possible_touch_pin in get_pin_names():  # Get the pin name.
    try:
        touch_pin_object = touchio.TouchIn(
            possible_touch_pin
        )  # Attempt to create the touch object on each pin.
        # Print the touch-capable pins that do not need, or already have, an external pulldown.
        print("Touch on:", str(possible_touch_pin).replace("board.", ""))
    except ValueError as error:  # A ValueError is raised when a pin is invalid or needs a pulldown.
        # Obtain the message associated with the ValueError.
        error_message = getattr(error, "message", str(error))
        if (
            "pulldown" in error_message  # If the ValueError is regarding needing a pulldown...
        ):
            print(
                "Touch on:", str(possible_touch_pin).replace("board.", "")
            )
        else:
            print("No touch on:", str(possible_touch_pin).replace("board.", ""))
    except TypeError:  # Error returned when checking a non-pin object in dir(board).
        pass  # Passes over non-pin objects in dir(board).

Now, connect to the serial console and check out the output! The results print out a nice handy list of pins that support capacitive touch.

Setting Touch Threshold Manually

In some cases, the ESP32-S3 fails to calibrate the touch threshold properly on board startup. In those cases, the board running this code will show the pin as touched (printed in the serial console) when you are not touching it. In this event, you can set the threshold manually. Setting the threshold manually involves two steps: obtaining the current touch threshold and raw_value, and setting the threshold once that information is known.

First, add the following to your code inside the loop (after the while True: which is included below for reference). The following code works exactly as-is for the one-pin example. Duplicate these two lines, and update touch to touch_one and touch_two for the two-pin example.

while True:
    print(touch.raw_value)
    print(touch.threshold)

Now, if you haven't already, connect to the serial console. You will see two new lines of information printed out. Make a mental note of what the current threshold is. The more important piece of information is the raw value. Note what the value is when you are not touching the pin. Now, touch the pin, and make a note of that value. The value you choose for updating threshold should be higher than when you are not touching the pin, and lower than when you are.

Once you've chosen your value, you can delete the lines you added above. Then, add the following line of code BEFORE the loop, but after the touch object is created. Replace NEW_VALUE with the value you chose based on the data provided. Again, for use with the two-pin example, duplicate these two lines, and update touch to touch_one and touch_two.

touch.threshold = NEW_VALUE

Now, your board should automatically reload, and you should see nothing printed to the serial console. Try touching the pin. Pin touched!

If you're still seeing it printing to the serial console when not touched, try changing the threshold value to something higher than you currently have.

That's all there is to manually setting the threshold for a touch pin!

This guide was first published on Aug 25, 2023. It was last updated on Jul 24, 2024.

This page (Capacitive Touch) was last updated on Jul 24, 2024.

Text editor powered by tinymce.