Hackster will be offline on Monday, June 15 from 5pm to 7pm PDT to perform some scheduled maintenance.
Alex Merchen
Published

Tap Board

It's like the good old days of AOL Instant Message - but physical.

14
Tap Board

Things used in this project

Hardware components

USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
×3
Raspberry Pi Pico W
Raspberry Pi Pico W
×3
Adafruit NeoPixel RGB 5050 LED with Integrated Driver Chip
×1
Capacitor Kit, 0805
Capacitor Kit, 0805
×1
0805 Resistor
×1
SN74LV1T125 Single Power Supply Single Buffer Gate with 3-State Output CMOS Logic Level Shifter
×1
TPS61023DRLR Boost Switching Regulator IC
×1
MCP73831-2-OT Miniature Single-Cell, Fully Integrated Li-Ion, Li-Polymer Charge Management Controllers
×1
Power Inductors - SMD Power Inductors - SMD WE-PMFI 1.0 uH 6.1 A 28 mOhms AEC-Q200
×1

Software apps and online services

Thonny
Adafruit IO

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Solder Flux, Soldering
Solder Flux, Soldering
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Step File to 3D Print Enclosure

Schematics

Schematic for Tapboard

Code

Main Running Code

Python
Change lines 75 to 79 and also line 145 to configure it for a different board (this one is for Feed 1/User 1)
# SPDX-FileCopyrightText: 2024 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import neopixel
import ssl
import wifi
import socketpool
import analogio
import adafruit_requests as requests

# --- Wi-Fi & Adafruit IO Credential Setup ---
try:
    from secrets import secrets
    WIFI_SSID = secrets['wifi_ssid']
    WIFI_PASSWORD = secrets['wifi_password']
    ADAFRUIT_IO_USERNAME = secrets['adafruit_io_username']
    ADAFRUIT_IO_KEY = secrets['adafruit_io_key']
except ImportError:
    print("Secrets file not found. Make sure secrets.py is on your CIRCUITPY drive.")
    raise

# --- NeoPixel Setup ---
pixel_pin = board.GP6
num_pixels = 21
pixels = neopixel.NeoPixel(
    pixel_pin, num_pixels, brightness=0.5, auto_write=False, pixel_order=neopixel.GRB
)

# --- SLOW Boot Sequence Function ---
def run_boot_animation(color=(0, 255, 100), speed=0.2): # Changed speed from 0.03 to 0.2
    """Slow chase animation to verify LEDs on startup."""
    print("Starting slow boot animation...")
    
    # Wipe on slowly
    for i in range(num_pixels):
        pixels[i] = color
        pixels.show()
        time.sleep(speed)
    
    # Hold for a moment when full
    time.sleep(0.5)
    
    # Wipe off slowly
    for i in range(num_pixels):
        pixels[i] = (0, 0, 0)
        pixels.show()
        time.sleep(speed)
    
    time.sleep(0.5) # Final pause before Wi-Fi hammers the CPU

# Trigger the slow animation
run_boot_animation()

# --- Analog Button Setup ---
analog_in = analogio.AnalogIn(board.GP26)
last_button_state = -1 

def get_voltage():
    raw_value = analog_in.value
    return (raw_value / 65535) * 3.3

def read_button():
    voltage = get_voltage()
    if 3.2 < voltage < 3.4:   return 1
    elif 2.9 < voltage < 3.1: return 2
    elif 2.1 < voltage < 2.3: return 3
    elif 1.55 < voltage < 1.75: return 4
    elif 1.0 < voltage < 1.2: return 5
    elif voltage < 0.1: return 0
    else: return -1

# --- Feed Configuration ---
feeds = {
    "favefive.feed1": {"color": (127, 0, 0), "calc": lambda v: v-1},
    "favefive.feed2": {"color": (0, 127, 0), "calc": lambda v: 10 - v},
    "favefive.feed3": {"color": (0, 0, 127), "calc": lambda v: 9 + v},
    "favefive.feed4": {"color": (127, 127, 0), "calc": lambda v: 20 - v},
}

# --- Wi-Fi & Connection Setup ---
print(f"Connecting to Wi-Fi network: {WIFI_SSID}...")
try:
    wifi.radio.connect(WIFI_SSID, WIFI_PASSWORD)
    print("Connected successfully!")
except ConnectionError as e:
    print(f"Failed to connect to Wi-Fi: {e}")
    raise

pool = socketpool.SocketPool(wifi.radio)
ssl_context = ssl.create_default_context()
http_session = requests.Session(pool, ssl_context)

# --- Adafruit IO API Helper Functions ---
def get_feed_data(feed_name):
    url = f"https://io.adafruit.com/api/v2/{ADAFRUIT_IO_USERNAME}/feeds/{feed_name}/data/last"
    headers = {"X-AIO-Key": ADAFRUIT_IO_KEY}
    try:
        with http_session.get(url, headers=headers) as response:
            if response.status_code == 200:
                return response.json().get("value")
            return None
    except Exception as e:
        print(f"Error fetching data: {e}")
        return None

def update_feed(feed_name, value):
    url = f"https://io.adafruit.com/api/v2/{ADAFRUIT_IO_USERNAME}/feeds/{feed_name}/data"
    headers = {"X-AIO-Key": ADAFRUIT_IO_KEY, "Content-Type": "application/json"}
    payload = {"value": value}
    try:
        with http_session.post(url, headers=headers, json=payload) as response:
            if response.status_code != 200:
                print(f"Error updating feed: {response.status_code}")
    except Exception as e:
        print(f"Error updating feed: {e}")

def update_pixel_display():
    print("\nUpdating NeoPixel display from IO feeds...")
    pixels.fill((0, 0, 0))
    for feed_name, feed_info in feeds.items():
        value_str = get_feed_data(feed_name)
        if value_str is not None:
            try:
                value = int(value_str)
                pixel_index = feed_info["calc"](value)
                if 0 <= pixel_index < num_pixels:
                    pixels[pixel_index] = feed_info["color"]
            except (ValueError, TypeError):
                continue
    pixels.show()

# --- Main Loop ---
last_pixel_update_time = 0
pixel_update_interval = 15 

while True:
    try:
        button_pressed = read_button()
        if button_pressed != last_button_state:
            last_button_state = button_pressed
            if button_pressed > 0:
                print(f"Button {button_pressed} pressed!")
                update_feed("favefive.feed1", button_pressed)
                update_pixel_display()
                last_pixel_update_time = time.monotonic()

        current_time = time.monotonic()
        if (current_time - last_pixel_update_time) >= pixel_update_interval:
            update_pixel_display()
            last_pixel_update_time = current_time

    except (OSError, requests.RequestError) as e:
        print(f"Network error: {e}. Reconnecting...")
        try:
            wifi.radio.connect(WIFI_SSID, WIFI_PASSWORD)
        except:
            pass

    time.sleep(0.1)

Credits

Alex Merchen
28 projects • 41 followers
I'm an EE with a Masters in ECE. I like building things.

Comments