Project 3.11: "Cooperative Game"
What you’ll learn
- Goal 1: Create a digital ping‑pong game with synchronized turns and score keeping.
- Goal 2: Add robot‑style feedback for a cooperative obstacle course (sensors + signals).
- Goal 3: Build a collaborative puzzle that requires both boards to share clues.
- Goal 4: Design a teamwork simulator with roles (Leader/Helper) and fair difficulty.
- Goal 5: Enable free creative play with safe defaults and clean communication.
Key ideas
- Coordination: Use simple messages to synchronize turns and actions.
- Signals: LEDs, buzzers, or LCD lines display live feedback to players.
- Fairness: Balance pace and penalties to keep the challenge fun and not frustrating.
- Clarity: Short messages, readable UI, and frequent but not spammy updates.
Blocks glossary
- Bluetooth peripheral/central: Send and receive short text messages for turns, scores, and clues.
- IR send/receive: Optional quick one‑way signals (serve, ping, sync).
- Digital input (pull‑up): Read buttons for serve, hit, confirm, or solve.
- Digital output: Drive LEDs/buzzer for feedback (hit, miss, win).
- LCD print (I2C): Show score, round, hints, and timers.
- Serial print: Minimal logs for debugging and fairness checks.
- def function: Helpers for send_msg(), set_led(), show_line(), and validate moves.
- Loop: Turn‑based cycles and steady pacing for cooperative play.
What you need
| Part | How many? | Example pins (R32) |
|---|---|---|
| D1 R32 (Board A) | 1 | Buttons A(26), B(25); LED 13; Bluetooth central |
| D1 R32 (Board B) | 1 | Buttons C(17), D(16); LED 13; Bluetooth peripheral (“Game‑R32”) |
| Optional LCD 1602 (I2C) | 1 | SCL 26, SDA 5, VCC 5V, GND |
| Optional sensors | 2–4 | IR RX 26; light sensor ADC2; touch switch on any digital pin |
- Keep boards within 1–3 m for reliable Bluetooth.
- If using IR, aim TX to RX with clear line of sight (20–50 cm).
- Share GND for any wired shared components (e.g., common buzzer circuit).
Before you start
- Open two serial monitors: Board A (Controller/Leader) and Board B (Peripheral/Helper).
- Quick test:
print("Ready!") # Confirm serial is working
🎮 Microprojects (5 mini missions)
🎮 Microproject 3.11.1 – Digital ping‑pong game (turn sync + scoring)
Goal: A “ball” moves back and forth as timed turns. Players press at the right moment to “return.” Misses give a point to the other side. Best of 7 wins.
# Microproject 3.11.1 – Ping-Pong Game (Board B = Peripheral, Board A = Central)
# Run this on Board B (Peripheral). Board A code follows below.
import ble_peripheral # Load Bluetooth peripheral helper
import ble_handle # Load Bluetooth callback helper
import machine # Load hardware pin library
import time # Load time library
ble_p = ble_peripheral.BLESimplePeripheral('Game-R32') # Peripheral named 'Game-R32'
handle = ble_handle.Handle() # Callback handle
print("[B] BLE 'Game-R32' ready") # Serial: Bluetooth initialized
led = machine.Pin(13, machine.Pin.OUT) # LED for hit/miss feedback
print("[B] LED on Pin 13 ready") # Serial: LED initialized
score_A = 0 # Score for Board A
score_B = 0 # Score for Board B
turn = "A" # Ball starts at A
round_active = False # Whether a rally is active
def set_led(on): # Helper: turn LED on/off
led.value(1 if on else 0) # LED state set by boolean
print("[B] LED:", "ON" if on else "OFF") # Serial: LED state log
def send(text): # Helper: send a Bluetooth message
ble_p.send(text) # Transmit text to Board A
print("[B] TX:", text) # Serial: transmission log
def start_round(): # Helper: start a rally
global round_active, turn # Use global round and turn
round_active = True # Mark rally active
turn = "A" # Serve to A
send("SERVE:A") # Notify A to serve
set_led(True) # LED on to mark start
def process_hit(player): # Helper: handle a hit
global turn # Use global turn state
if player == "A": # If A hits
turn = "B" # Ball goes to B
send("BALL:B") # Notify next receiver B
else: # Else B hits
turn = "A" # Ball goes to A
send("BALL:A") # Notify next receiver A
def process_miss(player): # Helper: handle a miss
global score_A, score_B, round_active # Use global scores and round
if player == "A": # If A missed
score_B += 1 # B gains a point
send("MISS:A|SCORE:" + str(score_A) + "-" + str(score_B)) # Notify miss and score
else: # If B missed
score_A += 1 # A gains a point
send("MISS:B|SCORE:" + str(score_A) + "-" + str(score_B)) # Notify miss and score
round_active = False # End rally
set_led(False) # LED off to mark end
def handle_method(msg): # Callback: receive events from A
s = str(msg) # Ensure string type
print("[B] RX:", s) # Serial: show incoming
if s == "SERVE?": # A asks to start
start_round() # Begin rally
elif s == "HIT:A" and round_active and turn == "A": # A returns correctly
process_hit("A") # Switch ball to B
elif s == "HIT:B" and round_active and turn == "B": # B returns correctly
process_hit("B") # Switch ball to A
elif s == "MISS:A" and round_active and turn == "A": # A misses
process_miss("A") # Score for B
elif s == "MISS:B" and round_active and turn == "B": # B misses
process_miss("B") # Score for A
elif s.startswith("RESET"): # Reset scores
score_A = 0 # Zero A score
score_B = 0 # Zero B score
send("ACK:RESET") # Acknowledge reset
handle.recv(handle_method) # Register callback
print("[B] Callback registered") # Serial: callback ready
while True: # Idle loop; reactions via callback
time.sleep_ms(150) # Keep CPU cool
# Microproject 3.11.1 – Ping-Pong Game (Board A = Central)
# Run this on Board A (Central). Uses two buttons to HIT or MISS.
import ble_central # Load Bluetooth central helper
import ble_handle # Load Bluetooth handle helper
import machine # Load hardware pin library
import time # Load time library
pin_hit = machine.Pin(26, machine.Pin.IN, machine.Pin.PULL_UP) # Button HIT
pin_miss = machine.Pin(25, machine.Pin.IN, machine.Pin.PULL_UP) # Button MISS
print("[A] Buttons HIT=26 MISS=25 ready") # Serial: buttons ready
central = ble_central.BLESimpleCentral() # Create central object
handle = ble_handle.Handle() # Create callback handle
print("[A] Central + handle ready") # Serial: Bluetooth initialized
def connect_peer(): # Helper: connect to peripheral
central.scan() # Scan for devices
time.sleep_ms(600) # Short scan wait
central.connect('Game-R32') # Connect by name
print("[A] Connected to Game-R32") # Serial: connection success
def send(text): # Helper: send command text
central.send(text) # Transmit message
print("[A] TX:", text) # Serial: send log
def handle_method(msg): # Callback: print peer feedback
print("[A] RX:", msg) # Serial: show incoming
handle.recv(handle_method) # Register callback
print("[A] Callback registered") # Serial: callback active
connect_peer() # Connect to Board B
send("SERVE?") # Ask peer to start a rally
turn = "A" # Track whose turn locally
while True: # Player input loop
if pin_hit.value() == 0: # If HIT button pressed (LOW)
send("HIT:" + turn) # Send HIT with current turn
turn = "B" if turn == "A" else "A" # Flip local turn expectation
time.sleep_ms(300) # Debounce press
if pin_miss.value() == 0: # If MISS button pressed (LOW)
send("MISS:" + turn) # Send MISS with current turn
turn = "B" if turn == "A" else "A" # Flip turn (rally ends in peer)
time.sleep_ms(300) # Debounce press
time.sleep_ms(40) # Short poll delay
Reflection: You synchronized a rally and kept fair score.
Challenge: Add “Best of 7” win message and auto reset when someone reaches 4 points.
🎮 Microproject 3.11.2 – Cooperative obstacle course (sensor feedback)
Goal: Board B reads a sensor (e.g., light on ADC2). Board A requests “GO” and gets PASS/FAIL and hints.
# Microproject 3.11.2 – Obstacle course (Board B = Peripheral sensor judge)
import ble_peripheral # Load Bluetooth peripheral helper
import ble_handle # Load Bluetooth callback helper
import machine # Load hardware ADC/pin library
import time # Load time library
ble_p = ble_peripheral.BLESimplePeripheral('Course-R32') # Peripheral named 'Course-R32'
handle = ble_handle.Handle() # Callback handle
print("[B] BLE 'Course-R32' ready") # Serial: Bluetooth initialized
adc2 = machine.ADC(machine.Pin(2)) # Light sensor on ADC2
adc2.atten(machine.ADC.ATTN_11DB) # Full voltage range
adc2.width(machine.ADC.WIDTH_12BIT) # 12-bit resolution
print("[B] ADC2 ready") # Serial: ADC ready
led = machine.Pin(13, machine.Pin.OUT) # LED for PASS/FAIL
print("[B] LED on 13 ready") # Serial: LED ready
THRESH = 1800 # Threshold for PASS
print("[B] THRESH =", THRESH) # Serial: show threshold
def send(text): # Helper: send feedback
ble_p.send(text) # Transmit text
print("[B] TX:", text) # Serial: send log
def judge(): # Helper: evaluate sensor
val = adc2.read() # Read ADC value
print("[B] ADC2:", val) # Serial: show reading
if val >= THRESH: # If above threshold
led.value(1) # LED ON for PASS
send("PASS:ADC2=" + str(val)) # Send PASS with value
else: # Otherwise fail
led.value(0) # LED OFF for FAIL
send("FAIL:ADC2=" + str(val) + "|HINT:More light") # Send FAIL with hint
def handle_method(msg): # Callback: process GO command
s = str(msg) # Ensure string
print("[B] RX:", s) # Serial: log RX
if s == "GO": # If GO command
judge() # Evaluate sensor and respond
elif s.startswith("THRESH="): # If threshold update
val = int(s.split("=")[1]) # Parse integer
global THRESH # Use global threshold
THRESH = val # Update threshold
send("ACK:THRESH=" + str(THRESH)) # Acknowledge change
handle.recv(handle_method) # Register callback
print("[B] Callback registered") # Serial: callback active
while True: # Idle loop; reacts via callback
time.sleep_ms(150) # Keep CPU cool
Reflection: You built a judge that gives clear PASS/FAIL with hints — cooperative feedback.
Challenge: Add a second sensor (touch switch) and require both conditions to PASS.
🎮 Microproject 3.11.3 – Collaborative puzzle (clues exchange)
Goal: Board A and B exchange clues; only combined input unlocks “SOLVE”.
# Microproject 3.11.3 – Collaborative puzzle (Board B = Peripheral keeps secret code)
import ble_peripheral # Load Bluetooth peripheral helper
import ble_handle # Load Bluetooth callback helper
import time # Load time library
ble_p = ble_peripheral.BLESimplePeripheral('Puzzle-R32') # Peripheral named 'Puzzle-R32'
handle = ble_handle.Handle() # Callback handle
print("[B] BLE 'Puzzle-R32' ready") # Serial: Bluetooth initialized
SECRET = "SUN" # Secret code held by Board B
partial_A = "" # Track A's input
partial_B = "" # Track B's side (if any)
print("[B] SECRET =", SECRET) # Serial: show secret
def send(text): # Helper: send message
ble_p.send(text) # Transmit message
print("[B] TX:", text) # Serial: log send
def handle_method(msg): # Callback: handle clues
global partial_A, partial_B # Use global partials
s = str(msg) # Ensure string
print("[B] RX:", s) # Serial: show message
if s.startswith("CLUE:"): # If a clue from A
partial_A = s.split(":")[1] # Store A's clue text
send("ACK:CLUE") # Acknowledge
elif s == "REQ:CLUE": # A requests a hint
send("HINT:Starts with 'S'") # Provide hint
elif s.startswith("TRY:"): # A attempts a solution
guess = s.split(":")[1] # Extract guess
if guess == SECRET: # If correct
send("SOLVE:OK") # Confirm success
else: # If incorrect
send("SOLVE:NO") # Deny
else: # Unknown input
send("ERR:UNKNOWN") # Send error feedback
handle.recv(handle_method) # Register callback
print("[B] Callback registered") # Serial: active
while True: # Idle; actions via callback
time.sleep_ms(200) # Keep CPU cool
Reflection: Clear clues and explicit SOLVE steps keep puzzles fair and fun.
Challenge: Add “TRIES left” counter and reduce on each wrong TRY.
🎮 Microproject 3.11.4 – Teamwork simulator (roles + timers + fairness)
Goal: Board A is Leader; Board B is Helper. Leader dispatches tasks; Helper confirms. Both track timers; no overload.
# Microproject 3.11.4 – Teamwork simulator (Board A = Leader, Board B = Helper)
# Run on Board A (Leader). Board B mirrors confirmations.
import ble_central # Load Bluetooth central helper
import ble_handle # Load Bluetooth handle helper
import machine # Load hardware pin library
import time # Load time library
btn_task = machine.Pin(26, machine.Pin.IN, machine.Pin.PULL_UP) # Button to send task
print("[A] Button TASK=26 ready") # Serial: button ready
central = ble_central.BLESimpleCentral() # Central object
handle = ble_handle.Handle() # Callback handle
print("[A] Central + handle ready") # Serial: Bluetooth initialized
def connect_peer(): # Helper: connect to helper device
central.scan() # Scan for devices
time.sleep_ms(600) # Short scan wait
central.connect('Helper-R32') # Connect by name
print("[A] Connected to Helper-R32") # Serial: connected
def send(text): # Helper: send text
central.send(text) # Transmit command
print("[A] TX:", text) # Serial: log send
def handle_method(msg): # Callback: log confirmations
print("[A] RX:", msg) # Serial: show incoming
handle.recv(handle_method) # Register callback
print("[A] Callback registered") # Serial: callback active
connect_peer() # Connect to Board B
last_task_ms = 0 # Last task dispatch time
gap_ms = 1000 # Min gap to avoid overload
def now_ms(): # Helper: current milliseconds
return time.ticks_ms() # Return ms ticks
while True: # Leader loop
if btn_task.value() == 0: # If task button pressed (LOW)
elapsed = time.ticks_diff(now_ms(), last_task_ms) # Check spacing
if elapsed >= gap_ms: # If enough time passed
send("TASK:SCAN") # Dispatch task
last_task_ms = now_ms() # Update dispatch time
time.sleep_ms(250) # Debounce button
else: # If too soon
print("[A] Throttle: wait", gap_ms - elapsed, "ms") # Serial: fairness
time.sleep_ms(40) # Short poll
# Microproject 3.11.4 – Teamwork simulator (Board B = Helper)
# Run on Board B (Helper). Confirms tasks with timers.
import ble_peripheral # Load Bluetooth peripheral helper
import ble_handle # Load Bluetooth callback helper
import machine # Load hardware pin library
import time # Load time library
ble_p = ble_peripheral.BLESimplePeripheral('Helper-R32') # Peripheral named 'Helper-R32'
handle = ble_handle.Handle() # Callback handle
print("[B] BLE 'Helper-R32' ready") # Serial: Bluetooth initialized
led = machine.Pin(13, machine.Pin.OUT) # LED indicates processing
print("[B] LED=13 ready") # Serial: LED ready
def send(text): # Helper: send confirmation
ble_p.send(text) # Transmit text
print("[B] TX:", text) # Serial: log send
def process_task(name): # Helper: simulate task
led.value(1) # LED ON (working)
print("[B] Task:", name) # Serial: log task name
time.sleep_ms(600) # Simulate work time
led.value(0) # LED OFF (done)
send("ACK:TASK=" + name) # Send acknowledgment
def handle_method(msg): # Callback: process task requests
s = str(msg) # Ensure string
print("[B] RX:", s) # Serial: show incoming
if s.startswith("TASK:"): # If a task instruction
process_task(s.split(":")[1]) # Extract task name and run
else: # Unknown request
send("ERR:UNKNOWN") # Send error feedback
handle.recv(handle_method) # Register callback
print("[B] Callback registered") # Serial: active
while True: # Idle loop
time.sleep_ms(150) # Keep CPU cool
Reflection: You modeled fair dispatch, helper confirmations, and simple throttling.
Challenge: Add “TASK:WAIT(ms)” so Helper reports progress every 200 ms until done.
🎮 Microproject 3.11.5 – Free creative play (safe defaults + hooks)
Goal: Provide a template both boards can modify: inputs to actions, logs, and feedback.
# Microproject 3.11.5 – Free play template (Board B = Peripheral ready for custom game)
import ble_peripheral # Load Bluetooth peripheral helper
import ble_handle # Load Bluetooth callback helper
import machine # Load hardware pin library
import time # Load time library
ble_p = ble_peripheral.BLESimplePeripheral('Play-R32') # Peripheral named 'Play-R32'
handle = ble_handle.Handle() # Callback handle
print("[B] BLE 'Play-R32' ready") # Serial: Bluetooth initialized
led = machine.Pin(13, machine.Pin.OUT) # LED feedback
btn = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_UP) # Button input
print("[B] LED=13, BTN=16 ready") # Serial: hardware ready
state = {"mode": "IDLE", "score": 0} # Simple mutable game state
print("[B] State init:", state) # Serial: show init state
def send(text): # Helper: send message
ble_p.send(text) # Transmit string
print("[B] TX:", text) # Serial: log send
def set_mode(m): # Helper: update mode safely
state["mode"] = m # Store new mode
send("ACK:MODE=" + m) # Acknowledge mode change
def handle_method(msg): # Callback: act on commands
s = str(msg) # Ensure string
print("[B] RX:", s) # Serial: log RX
if s == "MODE:PLAY": # Switch to play mode
set_mode("PLAY") # Update mode
elif s == "MODE:IDLE": # Switch to idle mode
set_mode("IDLE") # Update mode
elif s == "SCORE:+1": # Increase score
state["score"] += 1 # Change score
send("ACK:SCORE=" + str(state["score"])) # Confirm score
else: # Unknown command
send("ERR:UNKNOWN") # Error feedback
handle.recv(handle_method) # Register callback
print("[B] Callback registered") # Serial: active
while True: # Free play loop
if state["mode"] == "PLAY": # If in PLAY mode
if btn.value() == 0: # If button pressed
led.value(1) # LED ON (event)
send("EVENT:BTN") # Report event
time.sleep_ms(200) # Debounce
else: # If button not pressed
led.value(0) # LED OFF
time.sleep_ms(60) # Small loop delay
Reflection: A safe playground lets students experiment without breaking the structure.
Challenge: Add LCD and show “Mode” top row, “Score” bottom row, updating only when changed.
✨ Main project – Cooperative game suite (two boards, shared signals)
System outline
- Board A (Leader/Central): Sends turns, tasks, and solve attempts; reads buttons; keeps minimal logs.
- Board B (Helper/Peripheral): Judges timing, sensors, and puzzle; sends clear ACK/ERR; shows LEDs.
- Communication: Bluetooth text messages with short labels; optional IR for fast sync.
- Fairness: Debounce and spacing to avoid overload; explicit PASS/FAIL and hints.
# Project 3.11 – Cooperative Game Suite (A = Central Leader, B = Peripheral Helper)
# Includes ping-pong, obstacle judge, puzzle, and teamwork control via clean messages.
# ---------- BOARD B (Helper / Peripheral) ----------
import ble_peripheral # Load Bluetooth peripheral helper
import ble_handle # Load Bluetooth callback helper
import machine # Load hardware pin/ADC library
import time # Load time library
ble_p = ble_peripheral.BLESimplePeripheral('Coop-R32') # Peripheral named 'Coop-R32'
handle = ble_handle.Handle() # Callback handle
print("[B] BLE 'Coop-R32' ready") # Serial: Bluetooth initialized
led = machine.Pin(13, machine.Pin.OUT) # LED feedback
adc2 = machine.ADC(machine.Pin(2)) # Sensor on ADC2 (light/touch converter)
adc2.atten(machine.ADC.ATTN_11DB) # Full range
adc2.width(machine.ADC.WIDTH_12BIT) # Resolution
print("[B] LED=13, ADC2 ready") # Serial: hardware ready
scoreA = 0 # Ping-pong score A
scoreB = 0 # Ping-pong score B
turn = "A" # Ball starts A
round_active = False # Rally state
THRESH = 1800 # Obstacle threshold
SECRET = "SUN" # Puzzle code
print("[B] Init scores 0-0, turn=A, THRESH=1800, SECRET=SUN") # Serial: init state
def send(text): # Helper: transmit text
ble_p.send(text) # Send message
print("[B] TX:", text) # Serial: log send
def set_led(on): # Helper: LED toggle
led.value(1 if on else 0) # LED state
print("[B] LED:", "ON" if on else "OFF") # Serial: LED log
def start_rally(): # Helper: begin ping-pong rally
global round_active, turn # Use global states
round_active = True # Rally active
turn = "A" # Serve to A
send("SERVE:A") # Notify A to serve
set_led(True) # LED marks rally start
def hit(player): # Helper: handle HIT
global turn # Use global turn
if player == "A": # If A hit
turn = "B" # Ball to B
send("BALL:B") # Notify B turn
else: # Else B hit
turn = "A" # Ball to A
send("BALL:A") # Notify A turn
def miss(player): # Helper: handle MISS
global scoreA, scoreB, round_active # Use global scores
if player == "A": # If A missed
scoreB += 1 # B scores
send("MISS:A|SCORE:" + str(scoreA) + "-" + str(scoreB)) # Notify
else: # If B missed
scoreA += 1 # A scores
send("MISS:B|SCORE:" + str(scoreA) + "-" + str(scoreB)) # Notify
round_active = False # Rally ends
set_led(False) # LED off
def judge_obstacle(): # Helper: judge obstacle
val = adc2.read() # Read sensor
print("[B] ADC2:", val) # Serial: reading
if val >= THRESH: # If PASS
set_led(True) # LED ON
send("PASS:ADC2=" + str(val)) # PASS feedback
else: # Else FAIL
set_led(False) # LED OFF
send("FAIL:ADC2=" + str(val) + "|HINT:More light") # FAIL + hint
def handle_method(msg): # Callback: main protocol router
global THRESH # Use global threshold
s = str(msg) # Ensure string
print("[B] RX:", s) # Serial: incoming log
# Ping-pong control
if s == "SERVE?": # Start rally request
start_rally() # Begin rally
elif s == "HIT:A" and round_active and turn == "A": # A hits correctly
hit("A") # Switch ball to B
elif s == "HIT:B" and round_active and turn == "B": # B hits correctly
hit("B") # Switch ball to A
elif s == "MISS:A" and round_active and turn == "A": # A misses
miss("A") # Score B
elif s == "MISS:B" and round_active and turn == "B": # B misses
miss("B") # Score A
# Obstacle judge
elif s == "GO": # Judge obstacle now
judge_obstacle() # Evaluate sensor
elif s.startswith("THRESH="): # Threshold update
THRESH = int(s.split("=")[1]) # Parse new threshold
send("ACK:THRESH=" + str(THRESH)) # Ack threshold change
# Puzzle
elif s == "REQ:CLUE": # Request hint
send("HINT:Starts with 'S'") # Provide hint
elif s.startswith("TRY:"): # Attempt solution
guess = s.split(":")[1] # Extract guess
send("SOLVE:OK" if guess == SECRET else "SOLVE:NO") # Reply OK/NO
# Reset scores (optional)
elif s == "RESET": # Reset scores
scoreA = 0 # A=0
scoreB = 0 # B=0
send("ACK:RESET") # Ack reset
else: # Unknown message
send("ERR:UNKNOWN") # Error feedback
handle.recv(handle_method) # Register callback
print("[B] Callback registered") # Serial: callback active
while True: # Idle loop; all via callback
time.sleep_ms(150) # Keep CPU cool
# ---------- BOARD A (Leader / Central) ----------
import ble_central # Load Bluetooth central helper
import ble_handle # Load Bluetooth handle helper
import machine # Load hardware pin library
import time # Load time library
btn_hit = machine.Pin(26, machine.Pin.IN, machine.Pin.PULL_UP) # HIT button
btn_miss = machine.Pin(25, machine.Pin.IN, machine.Pin.PULL_UP) # MISS button
btn_go = machine.Pin(17, machine.Pin.IN, machine.Pin.PULL_UP) # GO judge
btn_try = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_UP) # TRY puzzle
print("[A] Buttons HIT=26 MISS=25 GO=17 TRY=16 ready") # Serial: buttons ready
central = ble_central.BLESimpleCentral() # Central object
handle = ble_handle.Handle() # Callback handle
print("[A] Central + handle ready") # Serial: Bluetooth initialized
def connect_peer(): # Helper: connect to Coop-R32
central.scan() # Scan for devices
time.sleep_ms(600) # Short scan delay
central.connect('Coop-R32') # Connect by name
print("[A] Connected to Coop-R32") # Serial: connection OK
def send(text): # Helper: transmit text
central.send(text) # Send command
print("[A] TX:", text) # Serial: log send
def handle_method(msg): # Callback: print feedback
print("[A] RX:", msg) # Serial: log incoming feedback
handle.recv(handle_method) # Register callback
print("[A] Callback registered") # Serial: callback active
connect_peer() # Connect to Board B
send("SERVE?") # Ask to start ping-pong rally
turn = "A" # Track ping-pong turn locally
guess = "SUN" # Example puzzle guess
while True: # Leader control loop
if btn_hit.value() == 0: # HIT pressed
send("HIT:" + turn) # Send HIT message
turn = "B" if turn == "A" else "A" # Flip expected turn
time.sleep_ms(250) # Debounce
if btn_miss.value() == 0: # MISS pressed
send("MISS:" + turn) # Send MISS message
turn = "B" if turn == "A" else "A" # Flip turn
time.sleep_ms(250) # Debounce
if btn_go.value() == 0: # GO judge pressed
send("GO") # Request obstacle judge
time.sleep_ms(250) # Debounce
if btn_try.value() == 0: # TRY puzzle pressed
send("TRY:" + guess) # Attempt puzzle solution
time.sleep_ms(250) # Debounce
time.sleep_ms(40) # Poll delay
External explanation
- What it teaches: How to structure small cooperative games with simple messages, fair timing, and clear feedback.
- Why it works: Bluetooth callbacks keep responses immediate; helpers clarify roles; debounced inputs prevent frustration; short labels keep bandwidth small.
- Key concept: Team play is about synchronization and feedback — your code mirrors those principles.
Story time
Two tiny teammates: one calls “serve!”, the other returns on time. Together they judge obstacles, trade clues, and confirm tasks. When everything clicks, the LEDs glow like high‑fives.
Debugging (2)
Debugging 3.11.A – Imbalance difficulty
- Symptom: Rally is too fast or the sensor threshold too strict.
- Fix: Add spacing and widen thresholds.
# In Board A: increase debounce to 300–350 ms for human timing
time.sleep_ms(300) # Friendlier input pacing
# In Board B: lower THRESH or make PASS window wider
THRESH = 1600 # More forgiving sensor threshold
Debugging 3.11.B – Inconsistent communication
- Symptom: Turns desync or messages feel out of order.
- Fix: Keep messages short and state‑dependent; log minimal context.
# Only accept HIT/MISS when round_active and matches current turn
elif s == "HIT:A" and round_active and turn == "A":
hit("A") # State-consistent action
Final checklist
- Ping‑pong rally starts on “SERVE” and alternates properly.
- Scores update fairly with clear MISS messages.
- Obstacle judge returns PASS/FAIL and a helpful hint.
- Puzzle accepts clues and responds SOLVE OK/NO.
- Teamwork simulator throttles tasks and confirms completion.
Extras
- Student tip: Rename devices (Game‑R32, Course‑R32, Coop‑R32) so pairing is quick.
- Instructor tip: Let students adjust THRESH and debounce to tune difficulty for their style.
- Glossary:
- Serve: Start a rally or turn.
- Hint: Guidance toward success without giving the answer.
- Debounce: A short delay to avoid repeated actions from one press.
- Mini tips:
- Keep messages under ~16 characters.
- Print concise serial logs; too many prints cause lag.
- Confirm actions with LEDs or short beeps for delight and clarity.