Bygg din egen spelkontroll

Av karl.svartholm@gmail.com / 0703-886860 (control.thatvoid.com).


Kontakta mig om du behöver delarna (200 kr), eller köp sjÀlv: Appendix B: Komponenter (blir lite dyrare).

Kontrollen fungerar som ett tangentbord och kan kopplas till dator eller Android-telefon / -tablet, kan fungera pÄ iOS & andra enheter - berÀtta om det funkar!

1. Anslut minidatorn (till datorn)

...se att minidatorn fÄr ström & startar.

đŸ§© Delar:

Koppla in USB-kabeln till datorn & ESP32:n.
...LED-lampan pÄ minidatorn ska blinka till.

2. SĂ€g "Hej" med minidatorn

...för att se att vi kan köra kod.

đŸ§© Delar: Thonny (program / kod-editor), 1 rad kod.

  1. Installer Thonny frÄn https://thonny.org/ & starta programmet
  2. Klicka lÀngst ner till höger i Thonny och vÀlj att du vill jobba med CircuitPython pÄ ESP:n.
  3. Skriv in kod c och tryck ▶ Run current script (F5).
  4. Texten Hej! ska komma upp under kodrutan (under Shell):

2. Kör ett program som fÄr datorns LED att lysa

...för att se att & hur saker funkar.
đŸ§© Delar: 4 rader kod.

  1. Kopiera koden & klistra in koden:
import board, neopixel
led = neopixel.NeoPixel(board.IO21, 1, brightness=0.1)

while True: # Programloop
    led.fill((0, 255, 255)) # fyll i en fÀrg
  1. Tryck ▶ Run current script (F5).

Eller öppna filen 2a-led-minimal.py (som igger pÄ ESP:n), nÀr frÄgad om varifrÄn vÀlj "CircuitPython device":

Tryck ▶ Run current script (F5).

Side Quests đŸ§™â€â™‚ïž

3. Koppla in en knapp

đŸ§© Delar: knapp, 2 sladdar (en jord / nolla & en för signal).

â„č Svarta sladdar anvĂ€nds normalt för jord / 0 volt / -
Röda sladdar anvÀnds normalt för ström, 3,3 volt för denna ESP32.

  1. Koppla (svart) sladd frÄn "-" pÄ knappen till gnd pÄ ESP:n (
  2. Den andra sladden till "S" (Signal) pÄ knappen.
  3. Svart sladd till "GND" pÄ ESP:n, se bilden nedan.
  4. Den andra sladden till "(PIN) 1" pÄ ESP:n, se bilden nedan.

â„č Breadborden (den vita grejen med hĂ„l i) ansluter pinnarna frĂ„n ESP:n sĂ„ som pilarna visar (att sĂ€tta i en stadd i ett av hĂ„len lĂ€ngs pilarna Ă€r alltsĂ„ samma sak som att sĂ€tta en sladd direkt pĂ„ pinnen).

4. FÄ knappen att tÀnda lampan

...för att se att knappen fungerar.

Delar đŸ§©

  1. Kopiera & klistra in koden:
import board, neopixel, digitalio

# StÀll in knappen
knapp = digitalio.DigitalInOut(board.IO1)    # IO1 = pinnen knappen sitter pÄ
knapp.direction = digitalio.Direction.INPUT  # vi ska lÀsa vÀrdet frÄn knappen (INPUT)
knapp.pull = digitalio.Pull.UP               # inbyggt motstÄnd, normalt "pÄ"

led = neopixel.NeoPixel(board.IO21, 1, brightness=0.1)  # LED pÄ pinne IO21

while True: # Programloop
    if not knapp.value:        # om knappen Àr nedtryckt
        led.fill((0, 0, 255))  # fyll i en fÀrg
    else:
        led.fill((0, 0, 0))    # slÀck
  1. Tryck ▶ Run current script (F5).
  2. Testa knappen.

Eller öppna & kör filen 4-knapp-tÀnder-led.py som ligger pÄ ESP:n.

Side Quests đŸ§™â€â™‚ïž

5. FĂ„ knappen att skicka mellanslag (SPACE)

Delar đŸ§©

  1. Kopiera och klistra in koden:
import board, neopixel, digitalio, time, usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

led = neopixel.NeoPixel(board.IO21, 1, brightness=0.1)  # LED pÄ pinne IO21

# StÀll in knappen
knapp = digitalio.DigitalInOut(board.IO1)    # IO1 = pinnen knappen sitter pÄ
knapp.direction = digitalio.Direction.INPUT  # vi ska lÀsa vÀrdet frÄn knappen (INPUT)
knapp.pull = digitalio.Pull.UP               # inbyggt motstÄnd, normalt "pÄ"

kbd = Keyboard(usb_hid.devices) # initiera USB-tangentbord

while True:
    if not knapp.value:         # om knappen Àr nedtryckt
        led.fill((0, 0, 255))   # fyll i en fÀrg
        kbd.send(Keycode.SPACE) # skicka space (tryck + slÀpp)
    else:
        led.fill((0, 0, 0))     # slÀck

    time.sleep(0.1) # liten paus: skicka space max 10 gÄnger / sekund
    
    # Nu kan du t ex spela chrome://dino eller flappybirds 
  1. Klicka Run.
  2. Testa till exempel: chrome://dino/

Eller öppna & kör filen 5-knapp-skickar-space.py som ligger pÄ ESP:n.

Side Quests đŸ§™â€â™‚ïž

6. Koppla in joystickens knapp

Delar đŸ§©: 1 Joystick, 3 sladdar: svart, röd & fĂ€rgglad

  1. Koppla den svarta sladden frÄn joystickens "GND" pinne till "GND" pÄ ESP:n.
  2. Koppla den röda sladden frĂ„n joystickens "+5V" till ESP:ns "3.3v" ⚠ Inte "5v" utan 3.3v --> 5v**.**
  3. Koppla den fÀrgglada sladden frÄn "SW" pÄ joysticken till "PIN2" pÄ ESP:n (eller nÀsta lediga nummerpinne om du kopplat in flera knappar).

7. FĂ„ joystickens knappen att skicka en tangent

Delar đŸ§©: ~7 rader kod

  1. Kan du klura ut koden sjÀlv?
  2. Annars lÀgg till den hÀr koden:
# JĂ€mte den andra knapp koden:
joy_knapp = digitalio.DigitalInOut(board.IO2)   # joystick-knapp
joy_knapp.direction = digitalio.Direction.INPUT
joy_knapp.pull = digitalio.Pull.UP              # hÄll "pÄ" nÀr knappen inte trycks

# Och i programmloopen:
    if not joy_knapp.value:
        kbd.send(Keycode.SPACE)
  1. Och kör.

Eller öppna filen 7-joystick-knapp-skickar-space.py.

8. Koppla in Joystickens x-axis (vÀnster till höger) & skriv ut vad den skickar

...sÄ att vi förstÄr vad för data / information som kommer frÄn joysticken.

Delar đŸ§©:

  1. Koppla sladden mellan joystickens "VRX" & ESP:ns PIN3 (eller nÀsta lediga nummer-pinne).
  2. Mata / kopiera in koden:
import board, neopixel, digitalio, time, usb_hid, analogio

x = analogio.AnalogIn(board.IO3)         # joystick X-axel

# och i Programmloopen:
    print(x.value)
  1. Kör programmet
  2. Se vad som skrivs ut under Shell i Thonny (editorn).
  3. Vilket vÀrde skrivs ut nÀr du inte rör pÄ den?
  4. ...och nÀr du trycker den lÀngst Ät varje sida?

Eller öppna och kör filen 8-joystick-skriv-ut-x.py.

9. Skicka vÀnster & höger nÀr Joystickens rörs (tillrÀckligt mycket

Delar đŸ§©: 4 rader kod

  1. Du kommer att behöva en if-sats som denna:
if NÅGOT NOGOT
  # Skicka en knapptryckning
elif NÅGOT NÅGOT
  # Skicka en annan knapptryckning
  1. Skriv den sjÀlv eller kopiera denna:
    if x.value < 25000:
        kbd.send(Keycode.LEFT_ARROW)
    elif x.value > 40000:
        kbd.send(Keycode.RIGHT_ARROW)
  1. Kör & testa koden 🙂
  2. Rör den sig Ät fel hÄll tycker du? I sÄ fall Àndra pÄ vilka knappar som skickas.

10. Gör samma sak för up/ner (y-axeln)

...kan du klura ut detta?

11. KĂ€nns kontrollern lite buggig?

...detta Àr för att knappar normalt sÀger till bÄde nÀr de trycks ner och slÀpps upp, just nu skickar vi bara en "knapptrycking" istÀllet (max 10 gÄnger per sekund). Testa den hÀr koden om du vill:

import time, board, digitalio, neopixel, usb_hid, analogio
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

led = neopixel.NeoPixel(board.IO21, 1, brightness=0.1)  # LED pÄ pinne IO21

# --- knapp för SPACE ---
knapp = digitalio.DigitalInOut(board.IO1)    # IO1 = pinnen knappen sitter pÄ
knapp.direction = digitalio.Direction.INPUT
knapp.pull = digitalio.Pull.UP              # hÄll pinnen "pÄ" nÀr knappen inte trycks

kbd = Keyboard(usb_hid.devices)  # initiera USB-tangentbord

# --- joystick-instÀllningar ---
joy_x = analogio.AnalogIn(board.IO3)         # joystick X-axel
joy_y = analogio.AnalogIn(board.IO4)         # joystick Y-axel
joy_knapp = digitalio.DigitalInOut(board.IO2)  # joystick-knapp
joy_knapp.direction = digitalio.Direction.INPUT
joy_knapp.pull = digitalio.Pull.UP           # hÄll "pÄ" nÀr knappen inte trycks

CENTER = 32768   # mittvÀrde för analog lÀsning (0..65535)
DÖDZON = 6000    # omrĂ„de runt mitten dĂ€r inget hĂ€nder

# Upprepningshastighet för SPACE nÀr knappen hÄlls nere
SPACE_REPEAT = 0.08  # sekunder mellan varje SPACE (0.08 ≈ 12.5 st / sekund)
senaste_space = 0.0

while True:
    nu = time.monotonic()

    # --- LÄS KNAPPAR ---
    knapp_tryckt = not knapp.value          # True = tryckt (pga pull-up)
    joy_tryckt = not joy_knapp.value        # True = tryckt

    # TÀnd LED om huvudknappen hÄlls nere
    led.fill((0, 0, 255) if knapp_tryckt else (0, 0, 0))

    # --- SPACE: HÅLLA NED = SPAMMA SPACE ---
    space_aktiv = knapp_tryckt or joy_tryckt

    if space_aktiv and (nu - senaste_space) >= SPACE_REPEAT:
        kbd.send(Keycode.SPACE)
        senaste_space = nu

    # --- JOYSTICK-RIKTNING ---

    # LÀs joystickens position (byt plats pÄ x/y om det kÀnns "fel")
    y = joy_x.value - CENTER
    x = joy_y.value - CENTER

    # VÀnster / Höger
    if x > DÖDZON:
        # höger
        kbd.press(Keycode.LEFT_ARROW)
        kbd.release(Keycode.RIGHT_ARROW)
    elif x < -DÖDZON:
        # vÀnster
        kbd.press(Keycode.RIGHT_ARROW)
        kbd.release(Keycode.LEFT_ARROW)
    else:
        # ingen riktning
        kbd.release(Keycode.LEFT_ARROW)
        kbd.release(Keycode.RIGHT_ARROW)

    # Upp / Ner
    if y > DÖDZON:
        # ner
        kbd.press(Keycode.DOWN_ARROW)
        kbd.release(Keycode.UP_ARROW)
    elif y < -DÖDZON:
        # upp
        kbd.press(Keycode.UP_ARROW)
        kbd.release(Keycode.DOWN_ARROW)
    else:
        # ingen riktning
        kbd.release(Keycode.UP_ARROW)
        kbd.release(Keycode.DOWN_ARROW)

    # Liten paus för att inte överbelasta allt
    time.sleep(0.003)


12. Se till att kontrollen funkar pÄ andra datorer

...sÄ att det körs varje gÄng kontrollern fÄr ström, oavsett var den ansluts.
Spara din fil som code.py (pÄ CIRCUITPY devicen), skriv över den gamla.

Extra: Kontroll med tvÄ joysticks

Den andra kan till exempel skicka W, A, S, D (istÀllet för piltangenter).
(Eventuellt kan vi senare göra en version dÀr ena joysticken funderar som en mus).

Delar đŸ§©:

  1. Koppla den svarta sladden frÄn joystickens "GND" pinne till "GND" pÄ ESP:n.
  2. Koppla den röda sladden frĂ„n joystickens "+5V" till ESP:ns "3.3v" ⚠ Inte "5v" utan 3.3v --> 5v**.**
  3. Koppla en fÀrgglad sladd frÄn "SW" pÄ joysticken till "PIN5" pÄ ESP:n
  4. Koppla en sladd mellan joystickens "VRX" & ESP:ns PIN6.
  5. Koppla en sladd mellan joystickens "VRY" & ESP:ns PIN7.
  6. Mata / kopiera in koden:
import time, board, digitalio, neopixel, usb_hid, analogio
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

led = neopixel.NeoPixel(board.IO21, 1, brightness=0.1)  # LED pÄ pinne IO21

# --- knapp för SPACE ---
knapp = digitalio.DigitalInOut(board.IO1)    # IO1 = pinnen knappen sitter pÄ
knapp.direction = digitalio.Direction.INPUT
knapp.pull = digitalio.Pull.UP              # hÄll pinnen "pÄ" nÀr knappen inte trycks

kbd = Keyboard(usb_hid.devices)  # initiera USB-tangentbord

# --- joystick-instÀllningar ---
joy_x = analogio.AnalogIn(board.IO3)         # joystick X-axel
joy_y = analogio.AnalogIn(board.IO4)         # joystick Y-axel
joy_knapp = digitalio.DigitalInOut(board.IO2)  # joystick-knapp
joy_knapp.direction = digitalio.Direction.INPUT
joy_knapp.pull = digitalio.Pull.UP           # hÄll "pÄ" nÀr knappen inte trycks

joy2_x = analogio.AnalogIn(board.IO6)         # joystick X-axel
joy2_y = analogio.AnalogIn(board.IO7)         # joystick Y-axel
joy2_knapp = digitalio.DigitalInOut(board.IO5)  # joystick-knapp
joy2_knapp.direction = digitalio.Direction.INPUT
joy2_knapp.pull = digitalio.Pull.UP           # hÄll "pÄ" nÀr knappen inte trycks


CENTER = 32768   # mittvÀrde för analog lÀsning (0..65535)
DÖDZON = 6000    # omrĂ„de runt mitten dĂ€r inget hĂ€nder

# Upprepningshastighet för SPACE nÀr knappen hÄlls nere
SPACE_REPEAT = 0.08  # sekunder mellan varje SPACE (0.08 ≈ 12.5 st / sekund)
senaste_space = 0.0

while True:
    nu = time.monotonic()

    # --- LÄS KNAPPAR ---
    knapp_tryckt = not knapp.value          # True = tryckt (pga pull-up)
    joy_tryckt = not joy_knapp.value        # True = tryckt
    joy2_tryckt = not joy2_knapp.value      # True = tryckt

    # TÀnd LED om huvudknappen hÄlls nere
    led.fill((0, 0, 255) if knapp_tryckt else (0, 0, 0))

    # --- SPACE: HÅLLA NED = SPAMMA SPACE ---
    space_aktiv = knapp_tryckt or joy_tryckt or joy2_tryckt

    if space_aktiv and (nu - senaste_space) >= SPACE_REPEAT:
        kbd.send(Keycode.SPACE)
        senaste_space = nu

    # --- JOYSTICK-RIKTNING ---

    # LÀs joystickens position (byt plats pÄ x/y om det kÀnns "fel")
    y = joy_x.value - CENTER
    x = joy_y.value - CENTER

    # VÀnster / Höger
    if x > DÖDZON:
        # höger
        kbd.press(Keycode.LEFT_ARROW)
        kbd.release(Keycode.RIGHT_ARROW)
    elif x < -DÖDZON:
        # vÀnster
        kbd.press(Keycode.RIGHT_ARROW)
        kbd.release(Keycode.LEFT_ARROW)
    else:
        # ingen riktning
        kbd.release(Keycode.LEFT_ARROW)
        kbd.release(Keycode.RIGHT_ARROW)

    # Upp / Ner
    if y > DÖDZON:
        # ner
        kbd.press(Keycode.DOWN_ARROW)
        kbd.release(Keycode.UP_ARROW)
    elif y < -DÖDZON:
        # upp
        kbd.press(Keycode.UP_ARROW)
        kbd.release(Keycode.DOWN_ARROW)
    else:
        # ingen riktning
        kbd.release(Keycode.UP_ARROW)
        kbd.release(Keycode.DOWN_ARROW)

    # --- JOYSTICK2-RIKTNING ---

    # LÀs joystickens position (byt plats pÄ x/y om det kÀnns "fel")
    y2 = joy2_x.value - CENTER
    x2 = joy2_y.value - CENTER

    # VÀnster / Höger
    if x2 > DÖDZON:
        # höger
        kbd.press(Keycode.A)
        kbd.release(Keycode.D)
    elif x2 < -DÖDZON:
        # vÀnster
        kbd.press(Keycode.D)
        kbd.release(Keycode.A)
    else:
        # ingen riktning
        kbd.release(Keycode.A)
        kbd.release(Keycode.D)

    # Upp / Ner
    if y2 > DÖDZON:
        # ner
        kbd.press(Keycode.S)
        kbd.release(Keycode.W)
    elif y2 < -DÖDZON:
        # upp
        kbd.press(Keycode.W)
        kbd.release(Keycode.S)
    else:
        # ingen riktning
        kbd.release(Keycode.W)
        kbd.release(Keycode.S)

    # Liten paus för att inte överbelasta allt
    time.sleep(0.003)

Appendix A: Gjort/gör i förvÀg med varje esp32-s3

  1. Installera CircuitPython frĂ„n https://circuitpython.org/board/waveshare_esp32_s3_zero/ kan göras frĂ„n webblĂ€sare, klicka "Open Installer đŸȘ„" & instruktionerna.
  2. Ladda ner Adafruit_CircuitPython_HID kod biblioteket och lÀgg hela katalogen adafruit_hid under katalogen lib pÄ esp32s usb device (ex D:\lib).
  3. Pack upp denna zip https://thatvoid.com/espyssel.zip & kopiera innehÄllet i katalogen code till usn:n (ex D:).

Appendix B: Komponenter {#appendix-b:-komponenter}

Montering

PS. Stiften har lötts pÄ ESP32n, det bör gÄ att hitta moduler som redan har stift.

Spel att testa med

https://gadiim.github.io/Space_Blast/
https://chromedino.com/
Space Waves đŸ•č Play on CrazyGameshttps://www.crazygames.com/game/space-waves (eventuellt)
Spel som kostar:
https://store.steampowered.com/app/1794680/Vampire_Survivors/
Megabonk (2 thumbsticks)

Vill du ha ett kit? {#vill-du-ha-ett-kit?}

Kontakta mig pÄ karl.svartholm@gmail.com eller 0703-886860 sÄ sÀtter jag ihop det för inköpspris.

Vill du se detta pÄ ett annat event?

Kontakta mig pÄ karl.svartholm@gmail.com eller 0703-886860.