📱 Level 5 – App Communication

Project 5.10: "Skill Game with App"

 

What you’ll learn

  • ✅ App control: Move a player in a simple maze using app commands (FWD, BACK, LEFT, RIGHT, STOP).
  • ✅ Reaction timing: Measure time between sensor/app signals and print a score.
  • ✅ Scoring system: Keep score, lives, and levels with clean app messages.
  • ✅ Feedback: Use LED/buzzer and OLED text to celebrate wins or show misses.
  • ✅ Multiplayer: Alternate turns between Player A and Player B and track their scores.

Key ideas

  • Short definition: A skill game is a loop of input → decision → feedback → score.
  • Real‑world link: Arcade games and training apps measure reaction and accuracy with instant feedback.

Blocks glossary (used in this project)

  • Bluetooth init + callback: Receive app messages and run code right away.
  • Variables: Store player position, score, lives, level, and current turn.
  • OLED shows / line / point: Draw maze walls and the player dot.
  • Digital output (LED/Buzzer): Turn ON/OFF to show win or miss.
  • Serial println: Print “KEY:VALUE” status for the app (SCORE, LIVES, LEVEL, TURN).

What you need

PartHow many?Pin connection
D1 R321USB cable (30 cm)
0.96″ OLED (128×64) SSD13061I2C: SCL → Pin 22, SDA → Pin 21, VCC, GND
10mm LED module1Signal → Pin 13, VCC, GND
Passive buzzer module1Signal → Pin 23, VCC, GND
Smartphone (Bluetooth)1Connect to device named “Clu‑Bots”

🔌 Wiring tip: OLED uses I2C on pins 22/21. Use Pin 13 for LED and Pin 23 for buzzer. Keep wires short and secure.
📍 Pin map snapshot: 22/21 (OLED), 13 (LED), 23 (buzzer). Other pins are free.


Before you start

  • USB cable is plugged in
  • Serial monitor is open
  • Test print shows:
print("Ready!")  # Confirm serial is working so you can see messages

Microprojects 1–5

Microproject 5.10.1 – App‑controlled maze game

Goal: Move a player dot around a simple maze using app commands; block movement at borders.
Blocks used:

  • BLE callback: Receive FWD/BACK/LEFT/RIGHT/STOP.
  • OLED draw: Walls and player dot.

Block sequence:

  1. Init OLED and draw maze borders.
  2. Track player (x,y).
  3. On app command, move if inside bounds.
  4. Redraw and print position.

MicroPython code:

import machine  # Import machine to access I2C and pins
import oled128x64  # Import OLED driver for SSD1306 128x64
import ble_central  # Import BLE central role to scan/connect
import ble_peripheral  # Import BLE peripheral role to advertise a name
import ble_handle  # Import BLE handler to receive messages

i2c = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000)  # Setup I2C bus on pins 22/21
oled = oled128x64.OLED(i2c, address=0x3c, font_address=0x3A0000, types=0)  # Initialize OLED at address 0x3C
print("Microproject 5.10.1: OLED ready")  # Status: OLED initialized

ble_c = ble_central.BLESimpleCentral()  # Create BLE central object
ble_p = ble_peripheral.BLESimplePeripheral('Clu-Bots')  # Create BLE peripheral named 'Clu-Bots'
ble_c.scan()  # Start scanning for peripherals
ble_c.connect(name='Clu-Bots')  # Connect to 'Clu-Bots' by name
print("BLE connected")  # Status: BLE connection established

player_x, player_y = 6, 6  # Set initial player position inside the screen
print("Player start:", player_x, player_y)  # Show starting position

walls = [(0,0,127,0), (0,63,127,63), (0,0,0,63), (127,0,127,63)]  # Define border walls (rectangle)
print("Walls defined:", len(walls))  # Confirm how many walls exist

def draw_maze():  # Function to draw the maze and player
    oled.clear()  # Clear the display before drawing
    for x1, y1, x2, y2 in walls:  # Loop through each wall segment
        oled.line(x1, y1, x2, y2, 1)  # Draw each wall line
    oled.shows('MAZE', x=0, y=0, size=1, space=0, center=False)  # Draw title text
    oled.point(player_x, player_y)  # Draw player as a single pixel
    oled.show()  # Refresh the screen so drawings appear
    print("Maze drawn; player at:", player_x, player_y)  # Confirm on serial

def in_bounds(nx, ny):  # Function to check screen bounds
    if nx < 1 or nx > 126 or ny < 1 or ny > 62:  # Check against border walls
        print("Blocked: out of bounds")  # Explain why movement fails
        return False  # Movement not allowed
    return True  # Movement allowed

def move(cmd):  # Function to handle movement commands
    global player_x, player_y  # Use global player coordinates
    nx, ny = player_x, player_y  # Start with current position
    if cmd == "FWD":  # If moving forward
        ny = player_y - 2  # Move up by 2 pixels
    elif cmd == "BACK":  # If moving backward
        ny = player_y + 2  # Move down by 2 pixels
    elif cmd == "LEFT":  # If moving left
        nx = player_x - 2  # Move left by 2 pixels
    elif cmd == "RIGHT":  # If moving right
        nx = player_x + 2  # Move right by 2 pixels
    elif cmd == "STOP":  # If stop command
        print("Stopped (no movement)")  # Acknowledge stop command
        return  # Exit with no movement
    if in_bounds(nx, ny):  # Check if new position is inside bounds
        player_x, player_y = nx, ny  # Apply movement
        print("Moved to:", player_x, player_y)  # Print new position
        draw_maze()  # Redraw maze with updated player position
    else:  # If movement is blocked
        print("No move: blocked")  # Explain why movement did not happen

def handle_method(key1, key2, key3, keyx):  # BLE receive callback for movement
    msg = str(key1)  # Convert incoming payload to text
    print("APP->R32:", msg)  # Show the received command
    move(msg)  # Try to move based on the command

handle = ble_handle.Handle()  # Create BLE handler object
handle.recv(handle_method)  # Attach the callback to receive BLE messages
draw_maze()  # Draw the initial maze and player

Reflection: Fast draw‑move‑redraw makes the game feel responsive and fun.
Challenge:

  • Easy: Change step size from 2 to 3 pixels for faster movement.
  • Harder: Add an inner wall line and prevent passing through it by adding a simple check.

Microproject 5.10.2 – Reaction game with sensors

Goal: Measure reaction time using two app events: START then STOP; print milliseconds.
Blocks used:

  • Timing: Use ticks_ms and ticks_diff.
  • Serial println: Print “REACTION(ms):…”.

Block sequence:

  1. Wait for “START”.
  2. Record start time.
  3. Wait for “STOP”.
  4. Compute and print reaction ms.

MicroPython code:

import time  # Import time to access tick functions

waiting = True  # Create a flag to indicate waiting for START
start_ms = 0  # Create a variable for the start timestamp
print("Microproject 5.10.2: Reaction game ready")  # Status message

def handle_method(key1, key2, key3, keyx):  # BLE callback to capture START/STOP
    global waiting  # Use global flag to manage game state
    global start_ms  # Use global timestamp to store START time
    msg = str(key1)  # Convert incoming payload to text
    print("APP->R32:", msg)  # Print the received command
    if msg == "START":  # If the START command arrives
        start_ms = time.ticks_ms()  # Record current ms as start time
        waiting = False  # Set flag to indicate we are timing now
        print("REACTION:START")  # Print status so user knows timing began
    elif msg == "STOP":  # If the STOP command arrives
        if waiting:  # If STOP comes without START first
            print("REACTION:NO_START")  # Print a helpful message
        else:  # If we have a valid START before
            end_ms = time.ticks_ms()  # Record current ms as end time
            reaction = time.ticks_diff(end_ms, start_ms)  # Compute ms difference
            print("REACTION(ms):", reaction)  # Print final reaction time
            waiting = True  # Reset flag to wait for next START

Reflection: Reaction games improve focus—try to beat your best time.
Challenge:

  • Easy: Track “BEST:” by keeping the lowest reaction ms seen.
  • Harder: Add a random delay before showing “START” in your app to prevent guessing.

Microproject 5.10.3 – Scoring and levels in the app

Goal: Keep score, lives, and level; level up every few points.
Blocks used:

  • Variables: score, lives, level.
  • Serial println: Print “SCORE”, “LIVES”, “LEVEL”.

Block sequence:

  1. Set score=0, lives=3, level=1.
  2. On “SCORE:+1” increase score and maybe level.
  3. On “MISS” lose a life; reset if lives=0.

MicroPython code:

score = 0  # Initialize score to zero
lives = 3  # Initialize lives to three
level = 1  # Initialize level to one
print("Microproject 5.10.3: SCORE:", score, "LIVES:", lives, "LEVEL:", level)  # Show initial stats

def handle_method(key1, key2, key3, keyx):  # BLE callback to update stats
    global score  # Use global score so changes persist
    global lives  # Use global lives so changes persist
    global level  # Use global level so changes persist
    msg = str(key1)  # Convert payload to text
    print("APP->R32:", msg)  # Print the command received
    if msg == "SCORE:+1":  # If player scores
        score += 1  # Add one point to score
        print("SCORE:", score)  # Print updated score
        if score % 5 == 0:  # If score is a multiple of 5
            level += 1  # Level up by one
            print("LEVEL:", level)  # Print updated level
    elif msg == "MISS":  # If player misses
        lives -= 1  # Subtract one life
        print("LIVES:", lives)  # Print remaining lives
        if lives <= 0:  # If no lives remain
            print("GAME:RESET")  # Print that game resets
            score, lives, level = 0, 3, 1  # Reset stats to starting values
            print("SCORE:", score, "LIVES:", lives, "LEVEL:", level)  # Print reset stats

Reflection: Levels make progress feel exciting—set fair rules and celebrate wins.
Challenge:

  • Easy: Level up every 3 points instead of 5.
  • Harder: Add “BONUS:+5” that increases score by 5 instantly.

Microproject 5.10.4 – Tactile and visual feedback

Goal: Use LED and buzzer for feedback; show “WIN!” or “MISS!” on OLED.
Blocks used:

  • Digital output: LED on Pin 13, buzzer on Pin 23.
  • OLED shows string: Draw quick messages.

Block sequence:

  1. On win → LED ON briefly + buzzer beep + “WIN!”.
  2. On miss → LED OFF + longer beep + “MISS!”.
  3. Keep messages short and readable.

MicroPython code:

import machine  # Import machine to access pins
import oled128x64  # Import OLED driver for SSD1306 128x64

led = machine.Pin(13, machine.Pin.OUT)  # Create LED output on Pin 13
buzzer = machine.Pin(23, machine.Pin.OUT)  # Create buzzer output on Pin 23
i2c = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000)  # Setup I2C on pins 22/21
oled = oled128x64.OLED(i2c, address=0x3c, font_address=0x3A0000, types=0)  # Initialize OLED display
print("Microproject 5.10.4: LED/Buzzer/OLED ready")  # Status: outputs ready

def feedback_win():  # Function to show win feedback
    led.value(1)  # Turn LED ON to celebrate
    buzzer.value(1)  # Turn buzzer ON for a short beep
    oled.clear()  # Clear OLED before drawing text
    oled.shows('WIN!', x=40, y=20, size=2, space=0, center=False)  # Draw big WIN text
    oled.show()  # Refresh the display so text appears
    print("Feedback: WIN!")  # Print status for the app
    buzzer.value(0)  # Turn buzzer OFF after beep
    led.value(0)  # Turn LED OFF to finish

def feedback_miss():  # Function to show miss feedback
    led.value(0)  # Ensure LED is OFF on miss
    buzzer.value(1)  # Turn buzzer ON for a longer beep
    oled.clear()  # Clear OLED before drawing text
    oled.shows('MISS!', x=35, y=20, size=2, space=0, center=False)  # Draw big MISS text
    oled.show()  # Refresh the display so text appears
    print("Feedback: MISS!")  # Print status for the app
    buzzer.value(0)  # Turn buzzer OFF after beep

Reflection: Instant light/sound makes your game feel alive—players know results right away.
Challenge:

  • Easy: Keep “WIN!” on screen for 500 ms with a delay in your app flow.
  • Harder: Draw a star for wins and a cross for misses using lines and points.

Microproject 5.10.5 – Turn‑based multiplayer

Goal: Alternate turns between Player A and Player B; add points to the current player.
Blocks used:

  • Variables: turn, scoreA, scoreB.
  • Serial println: Print “TURN:A/B”, “SCORE_A”, “SCORE_B”.

Block sequence:

  1. Start with turn=”A”, scoreA=0, scoreB=0.
  2. On “NEXT” switch turn.
  3. On “SCORE:+1” add to the current player.

MicroPython code:

turn = "A"  # Initialize turn to Player A
scoreA, scoreB = 0, 0  # Initialize scores for Player A and B
print("Microproject 5.10.5: Turn=A, A=0, B=0")  # Status: multiplayer ready

def handle_method(key1, key2, key3, keyx):  # BLE callback for turn-based play
    global turn  # Use global turn to update between callbacks
    global scoreA  # Use global scoreA to persist changes
    global scoreB  # Use global scoreB to persist changes
    msg = str(key1)  # Convert payload to text
    print("APP->R32:", msg)  # Print received command
    if msg == "NEXT":  # If the NEXT turn command arrives
        turn = "B" if turn == "A" else "A"  # Toggle turn between A and B
        print("TURN:", turn)  # Print current player's turn
    elif msg == "SCORE:+1":  # If a point is scored
        if turn == "A":  # If it's Player A's turn
            scoreA += 1  # Add one point to A
            print("SCORE_A:", scoreA)  # Print A's score
        else:  # If it's Player B's turn
            scoreB += 1  # Add one point to B
            print("SCORE_B:", scoreB)  # Print B's score

Reflection: Fair turns keep the game friendly—everyone gets a chance to shine.
Challenge:

  • Easy: Show a big “A” or “B” on the OLED to mark the turn.
  • Harder: Add a “WINNER?” command that prints who leads right now.

Main project – Skill game with app

Blocks steps (with glossary)

  • BLE init + callback: Receive app commands for movement, scoring, reaction, and turns.
  • OLED: Draw maze borders and the player dot plus small stats.
  • LED/Buzzer: Provide immediate win/miss feedback.
  • Score/levels: Track score, lives, and level on serial for the app.
  • Multiplayer: Alternate turns and track separate player scores.

Block sequence:

  1. Init BLE and attach callbacks.
  2. Setup OLED, LED, buzzer, and maze data.
  3. Move player on commands and check bounds.
  4. Run reaction game and scoring rules with feedback.
  5. Alternate turns and print scoreboard.

MicroPython code (mirroring blocks)

# Project 5.10 – Skill Game with App

import machine  # Import machine for pins and I2C hardware
import oled128x64  # Import OLED driver for SSD1306 128x64
import ble_central  # Import BLE central role
import ble_peripheral  # Import BLE peripheral role
import ble_handle  # Import BLE handler for callbacks
import time  # Import time for reaction timing and delays

# Setup OLED hardware
i2c = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000)  # I2C bus on pins 22/21
oled = oled128x64.OLED(i2c, address=0x3c, font_address=0x3A0000, types=0)  # OLED object at address 0x3C
print("OLED ready")  # Confirm OLED setup

# Setup LED and buzzer on outputs
led = machine.Pin(13, machine.Pin.OUT)  # LED output on Pin 13
buzzer = machine.Pin(23, machine.Pin.OUT)  # Buzzer output on Pin 23
print("LED/Buzzer ready")  # Confirm outputs

# Setup BLE connection
ble_c = ble_central.BLESimpleCentral()  # BLE central instance
ble_p = ble_peripheral.BLESimplePeripheral('Clu-Bots')  # BLE peripheral named 'Clu-Bots'
ble_c.scan()  # Scan for BLE peripheral
ble_c.connect(name='Clu-Bots')  # Connect to peripheral by name
print("BLE connected")  # Confirm BLE connection

# Maze state and player position
player_x, player_y = 6, 6  # Player position inside bounds
walls = [(0,0,127,0), (0,63,127,63), (0,0,0,63), (127,0,127,63)]  # Border walls (rectangle)
print("Maze loaded")  # Confirm maze

# Game stats
score, lives, level = 0, 3, 1  # Score, lives, level
turn = "A"  # Current player's turn
scoreA, scoreB = 0, 0  # Multiplayer scores
print("Stats -> SCORE:", score, "LIVES:", lives, "LEVEL:", level, "TURN:", turn)  # Print stats

# Reaction game flags
waiting = True  # Waiting for START signal
start_ms = 0  # Holder for start timestamp
print("Reaction initialized")  # Confirm reaction setup

def draw_maze():  # Draw maze and HUD
    oled.clear()  # Clear screen before drawing
    for x1, y1, x2, y2 in walls:  # Loop through all wall segments
        oled.line(x1, y1, x2, y2, 1)  # Draw each wall line
    oled.point(player_x, player_y)  # Draw player dot
    hud = 'S:'+str(score)+' L:'+str(lives)+' Lv:'+str(level)+' T:'+turn  # Compose HUD text
    oled.shows(hud, x=0, y=54, size=1, space=0, center=False)  # Write HUD line
    oled.show()  # Refresh display
    print("HUD:", hud, "POS:", player_x, player_y)  # Print HUD and position

def in_bounds(nx, ny):  # Bounds check for movement
    if nx < 1 or nx > 126 or ny < 1 or ny > 62:  # Limit inside border
        print("Blocked: bounds")  # Explain blocking
        return False  # Deny movement
    return True  # Allow movement

def move(cmd):  # Movement handler
    global player_x, player_y  # Use global player position
    step = 2  # Movement step in pixels
    nx, ny = player_x, player_y  # Create candidate position
    if cmd == "FWD":  # Forward (up)
        ny -= step  # Move up
    elif cmd == "BACK":  # Back (down)
        ny += step  # Move down
    elif cmd == "LEFT":  # Left
        nx -= step  # Move left
    elif cmd == "RIGHT":  # Right
        nx += step  # Move right
    elif cmd == "STOP":  # Stop command
        print("STOP received")  # Print stop
        return  # Exit with no change
    if in_bounds(nx, ny):  # Check bounds
        player_x, player_y = nx, ny  # Apply movement
        print("Moved:", player_x, player_y)  # Print new position
        draw_maze()  # Redraw maze
    else:  # Out of bounds
        print("No move (blocked)")  # Explain block

def feedback_win():  # Win feedback
    led.value(1)  # LED ON
    buzzer.value(1)  # Buzzer ON
    oled.clear()  # Clear display
    oled.shows('WIN!', x=40, y=20, size=2, space=0, center=False)  # Big WIN text
    oled.show()  # Refresh display
    print("WIN!")  # Print win
    buzzer.value(0)  # Buzzer OFF
    led.value(0)  # LED OFF

def feedback_miss():  # Miss feedback
    led.value(0)  # LED OFF
    buzzer.value(1)  # Buzzer ON
    oled.clear()  # Clear display
    oled.shows('MISS!', x=35, y=20, size=2, space=0, center=False)  # Big MISS text
    oled.show()  # Refresh
    print("MISS!")  # Print miss
    buzzer.value(0)  # Buzzer OFF

def add_score(points):  # Score increment
    global score, level  # Use global stats
    score += points  # Increase score
    print("SCORE:", score)  # Print score
    if score % 5 == 0:  # Every 5 points level up
        level += 1  # Increase level
        print("LEVEL:", level)  # Print level
    draw_maze()  # Redraw HUD

def miss_life():  # Life decrement
    global lives, score, level  # Use global stats
    lives -= 1  # Decrease lives
    print("LIVES:", lives)  # Print lives
    if lives <= 0:  # If no lives left
        print("GAME:RESET")  # Announce reset
        score, lives, level = 0, 3, 1  # Reset stats
        print("SCORE:", score, "LIVES:", lives, "LEVEL:", level)  # Print reset stats
    draw_maze()  # Redraw HUD

def handle_method(key1, key2, key3, keyx):  # BLE receive callback
    global waiting  # Use reaction flag
    global start_ms  # Use reaction timestamp
    global turn  # Use current turn
    global scoreA, scoreB  # Use multiplayer scores
    msg = str(key1)  # Convert payload to text
    print("APP->R32:", msg)  # Print received command

    # Movement commands
    if msg in ("FWD", "BACK", "LEFT", "RIGHT", "STOP"):  # Movement set
        move(msg)  # Execute movement
        return  # End this callback

    # Reaction game
    if msg == "START":  # Start timing
        start_ms = time.ticks_ms()  # Record start ms
        waiting = False  # Mark timing started
        print("REACTION:START")  # Print status
        return  # End branch
    if msg == "STOP":  # Stop timing
        if waiting:  # If no start
            print("REACTION:NO_START")  # Explain issue
        else:  # Valid stop
            end_ms = time.ticks_ms()  # Record end ms
            reaction = time.ticks_diff(end_ms, start_ms)  # Compute reaction time
            print("REACTION(ms):", reaction)  # Print result
            waiting = True  # Reset to waiting
        return  # End branch

    # Scoring commands
    if msg == "SCORE:+1":  # Add point
        add_score(1)  # Update score
        feedback_win()  # Show win feedback
        if turn == "A":  # If Player A
            scoreA += 1  # Increase A score
            print("SCORE_A:", scoreA)  # Print A score
        else:  # If Player B
            scoreB += 1  # Increase B score
            print("SCORE_B:", scoreB)  # Print B score
        return  # End branch
    if msg == "MISS":  # Miss event
        miss_life()  # Decrease life
        feedback_miss()  # Show miss feedback
        return  # End branch

    # Turn switching
    if msg == "NEXT":  # Switch turn
        turn = "B" if turn == "A" else "A"  # Toggle turn
        print("TURN:", turn)  # Print current turn
        draw_maze()  # Update HUD with new turn
        return  # End branch

    # Status query
    if msg == "STATUS?":  # Ask for status
        print("STATUS:SCORE", score)  # Print score
        print("STATUS:LIVES", lives)  # Print lives
        print("STATUS:LEVEL", level)  # Print level
        print("STATUS:TURN", turn)  # Print turn
        print("STATUS:A", scoreA)  # Print Player A score
        print("STATUS:B", scoreB)  # Print Player B score
        return  # End branch

    # Unknown command
    print("CMD:UNKNOWN", msg)  # Report unrecognized command

handle = ble_handle.Handle()  # Create BLE handler
handle.recv(handle_method)  # Attach the BLE receive callback
draw_maze()  # Draw initial maze and HUD

# Heartbeat loop to keep app synced
while True:  # Main heartbeat loop
    print("HB:SCORE", score)  # Print heartbeat score
    print("HB:LIVES", lives)  # Print heartbeat lives
    print("HB:LEVEL", level)  # Print heartbeat level
    print("HB:TURN", turn)  # Print heartbeat turn
    time.sleep(2)  # Small delay for readability

External explanation

  • What it teaches: You built a mini‑game controlled by an app: movement in a maze, reaction timing, scoring/levels, feedback, and multiplayer turns.
  • Why it works: BLE callbacks turn app taps into instant actions; OLED shows the game state; LED/buzzer deliver quick feedback; variables track progress; short status prints keep the app display in sync.
  • Key concept: “Input → update → feedback → score.”

Story time

Your board just became a pocket arcade. You tap—your player dashes. You react—WIN! The score climbs. Switch turns—your friend takes over. Tiny screen, big fun.


Debugging (2)

Debugging 5.10.1 – Difficulty not adjusted

Problem: Movement feels too easy or too hard at higher levels.
Clues: Player always moves by the same step, no matter the level.
Broken code:

step = 2  # Fixed step size that never changes

Fixed code:

step = 1 + (level // 2)  # Increase step every 2 levels to scale difficulty
print("Step size:", step)  # Print the current step for clarity

Why it works: Tying movement step to level makes the game ramp up in challenge naturally.
Avoid next time: Connect difficulty to variables (level, score) instead of fixed numbers.

Debugging 5.10.2 – Game elements are not responding

Problem: App sends commands but nothing changes on the screen.
Clues: Serial shows APP->R32, but the player doesn’t move or feedback doesn’t trigger.
Broken code:

def handle_method(...):
    msg = str(key1)  # Read message
    # move(msg)  # Forgot to call the movement function

Fixed code:

def handle_method(...):
    msg = str(key1)  # Read message
    move(msg)  # Call movement so position updates and OLED redraws
    print("Action executed for:", msg)  # Confirm execution on serial

Why it works: Executing the action updates the state and redraws visuals right away.
Avoid next time: Ensure every command path triggers the correct function.


Final checklist

  • App movement changes the player’s position in the maze
  • Reaction game prints milliseconds and resets correctly
  • SCORE/LIVES/LEVEL update with clear prints
  • LED/Buzzer/OLED feedback responds to WIN/MISS
  • NEXT alternates turns and A/B scores update

Extras

  • 🧠 Student tip: Add a “SAFE” mode that ignores movement for 3 seconds after MISS.
  • 🧑‍🏫 Instructor tip: Have students write rules (scoring, levels, turns) before coding—clarity prevents bugs.
  • 📖 Glossary:
    • Reaction time: Milliseconds between “START” and “STOP.”
    • Level: Difficulty stage that changes game behavior.
    • Feedback: Immediate signals (LED/buzzer/OLED) that show success or failure.
  • 💡 Mini tips:
    • Keep status messages short and labeled for easy app parsing.
    • Redraw the OLED only when something changes to stay responsive.
    • Tune step size and lives to match players’ skill.
On this page