Project 5.6: "R32-App-PC Communication"
What you’ll learn
- ✅ R32 ↔ App (Bluetooth): Send and receive messages both ways.
- ✅ R32 ↔ PC (Serial): Talk to your computer using the serial monitor.
- ✅ App ↔ PC bridge: Use the R32 as a messenger to relay data between the app and the PC.
- ✅ Sync data: Keep App and PC values consistent.
- ✅ Simultaneous control: Accept commands from both App and PC without confusion.
Key ideas
- Short definition: The R32 is a translator—it listens on Bluetooth and Serial and forwards messages to keep everyone in sync.
- Real-world link: Routers and hubs forward data between devices so everything stays connected.
Blocks glossary (used in this project)
- Bluetooth central/peripheral init: Start BLE roles so devices can discover and connect.
- Scan / connect by name: Central role finds and connects to “Clu-Bots.”
- Receive callback: A function that runs when a Bluetooth message arrives.
- Serial print / read input: Show messages on the PC and read what you type.
- Variables: Store “last_app_msg”, “last_pc_msg”, and “mode”.
- If / else: Decide where to forward messages (App → PC, PC → App).
What you need
| Part | How many? | Pin connection |
|---|---|---|
| D1 R32 | 1 | USB cable (30 cm) |
| Smartphone with Bluetooth | 1 | Bluetooth enabled |
| PC with Serial Monitor | 1 | USB connected |
🔌 Wiring tip: No extra wiring—Bluetooth and Serial are built-in.
📍 Pin map snapshot: All pins free; we focus on communication only.
Before you start
- USB cable is plugged in
- Serial monitor is open
- Test print shows:
print("Ready!") # Confirm serial is working
Microprojects (5 mini missions)
Microproject 5.6.1 – R32–App bidirectional communication
Goal: Connect BLE and print messages received from the app; prepare a reply string.
Blocks used:
- Init BLE central/peripheral: Start Bluetooth roles.
- Scan/connect: Join “Clu-Bots.”
- Receive callback: Handle incoming messages.
- Serial println: Show what arrived.
Block sequence:
- Init central + peripheral (“Clu-Bots”).
- Scan and connect by name.
- Attach receive callback.
- Print incoming and prepare a reply.
MicroPython code:
import ble_central # Import Bluetooth central role
import ble_peripheral # Import Bluetooth peripheral role
import ble_handle # Import handler for receive callbacks
ble_c = ble_central.BLESimpleCentral() # Create central (scanner/connector)
ble_p = ble_peripheral.BLESimplePeripheral('Clu-Bots') # Create peripheral named 'Clu-Bots'
print("Microproject 5.6.1: BLE init complete") # Status: BLE initialized
ble_c.scan() # Start scanning for peripherals
print("Scanning for 'Clu-Bots'...") # Tell student we are scanning
ble_c.connect(name='Clu-Bots') # Attempt to connect by name
print("Connecting to 'Clu-Bots'...") # Connection attempt message
last_app_msg = "" # Store last message from app
print("last_app_msg initialized") # Confirm variable setup
def handle_method(key1, key2, key3, keyx): # Define BLE receive callback
msg = str(key1) # Convert main payload to text
print("APP->R32:", msg) # Show message from app
global last_app_msg # Declare we modify the global variable
last_app_msg = msg # Save last app message for later use
print("Reply prepared: OK") # Show planned reply (example)
handle = ble_handle.Handle() # Create callback handle manager
handle.recv(handle_method) # Attach callback so messages trigger
print("BLE callback attached") # Confirm setup
Reflection: Callbacks are instant: when the app sends data, your function runs immediately.
Challenge:
- Easy: Change the reply word from “OK” to “RECEIVED”.
- Harder: Add a timestamp using system time to each received message.
Microproject 5.6.2 – R32–PC bidirectional communication (Serial)
Goal: Print status and read a line typed in the serial monitor.
Blocks used:
- Serial println: Show instructions and echoes.
- Serial read input: Ask the PC user for a message.
Block sequence:
- Print “Type something”.
- Read input().
- Echo it back.
MicroPython code:
print("Microproject 5.6.2: Type a message in the serial monitor") # Prompt the user
pc_msg = input("PC->R32: ") # Read a line from the PC user
print("PC message received:", pc_msg) # Echo the PC message back
Reflection: Serial is your friendly console—great for testing and debugging.
Challenge:
- Easy: Print the length of the message.
- Harder: If message is “STATUS?”, print “OK”.
Microproject 5.6.3 – App–PC communication bridge via R32
Goal: Forward messages: App → PC and PC → App (format-ready).
Blocks used:
- Variables: last_app_msg, last_pc_msg.
- Serial println: Forward and label messages.
- If / else: Decide the direction correctly.
Block sequence:
- Read app message into last_app_msg (already done by callback).
- Read PC message using input().
- Print “BRIDGE APP->PC: …” and “BRIDGE PC->APP: …”
MicroPython code:
last_app_msg = "HELLO_APP" # Example placeholder if no app msg yet
print("Microproject 5.6.3: Bridge ready") # Status message
pc_msg = input("PC->R32: ") # Read from PC
print("BRIDGE APP->PC:", last_app_msg) # Forward app message to PC
print("BRIDGE PC->APP:", pc_msg) # Forward PC message (to app display later)
Reflection: Clear labels make bridging simple—everyone knows who said what.
Challenge:
- Easy: Add prefixes like “APP:” and “PC:” to every line.
- Harder: If either message is empty, print “NO DATA”.
Microproject 5.6.4 – Data synchronization between App and PC
Goal: Keep a shared variable synced from both sides.
Blocks used:
- Variables: shared_value.
- If / else: Update from the newest source.
- Serial println: Show the sync result.
Block sequence:
- Parse app command like “SET:42”.
- PC can send “SET:99”.
- Update shared_value and print “SYNC:42”.
MicroPython code:
shared_value = 0 # Start shared value at 0
print("Microproject 5.6.4: shared_value =", shared_value) # Show initial value
app_cmd = "SET:42" # Simulate app command format
print("Simulated APP cmd:", app_cmd) # Show simulated app command
pc_cmd = input("PC command (e.g., SET:99): ") # Read PC command
print("Received PC cmd:", pc_cmd) # Echo PC command
if app_cmd.startswith("SET:"): # Check app set format
shared_value = int(app_cmd.split(":")[1]) # Update from app
print("SYNC from APP ->", shared_value) # Show sync
if pc_cmd.startswith("SET:"): # Check PC set format
shared_value = int(pc_cmd.split(":")[1]) # Update from PC
print("SYNC from PC ->", shared_value) # Show sync
print("Final shared_value:", shared_value) # Confirm result
Reflection: Using the same “KEY:VALUE” format from both sides keeps data clean.
Challenge:
- Easy: Accept “ADD:5” to increase the value.
- Harder: Add “WHO?” to print where the last change came from.
Microproject 5.6.5 – Simultaneous control from App and PC
Goal: Accept commands from both sources safely, preferring App if both arrive.
Blocks used:
- Variables: last_app_msg, last_pc_msg.
- If / else: Priority selection.
- Serial println: Show the chosen command.
Block sequence:
- Set priority = “APP”.
- Read PC input, check last_app_msg.
- Choose command based on priority.
- Print chosen command.
MicroPython code:
priority = "APP" # Choose who wins when both send data
print("Microproject 5.6.5: Priority =", priority) # Show priority
last_app_msg = "FWD" # Example app message (simulate if none yet)
print("Simulated last_app_msg:", last_app_msg) # Show app message
last_pc_msg = input("PC command (e.g., STOP): ") # Read PC command
print("Received last_pc_msg:", last_pc_msg) # Echo PC command
if priority == "APP": # If app should win conflicts
chosen = last_app_msg if last_app_msg else last_pc_msg # Prefer app message
print("Chosen command (APP priority):", chosen) # Show chosen command
else: # If PC should win
chosen = last_pc_msg if last_pc_msg else last_app_msg # Prefer PC message
print("Chosen command (PC priority):", chosen) # Show chosen command
Reflection: Priority rules prevent chaos—decide who wins and stick to it.
Challenge:
- Easy: Change priority to “PC”.
- Harder: Add “LOCK” state to ignore new commands for 2 seconds after choosing one.
Main project – R32–App–PC communication
Blocks steps (with glossary)
- BLE init / scan / connect: Start central + peripheral, connect by name.
- Receive callback: Store last_app_msg when messages arrive.
- Serial input/output: Read PC lines and print status.
- Bridge logic: Forward App->PC and PC->App with labels.
- Sync + priority: Update shared_value and choose command safely.
Block sequence:
- Init BLE (central + peripheral “Clu-Bots”), scan, connect.
- Attach receive callback to update last_app_msg.
- Read PC input and echo it.
- Bridge both ways with clear labels.
- Sync using SET:xx format; pick command using priority.
MicroPython code (mirroring blocks)
# Project 5.6 – R32-App-PC Communication
import ble_central # Start BLE central role (scan/connect)
import ble_peripheral # Start BLE peripheral role (advertise name)
import ble_handle # Handle BLE message callbacks
ble_c = ble_central.BLESimpleCentral() # Create central device
ble_p = ble_peripheral.BLESimplePeripheral('Clu-Bots') # Create peripheral named 'Clu-Bots'
print("BLE ready: Central + Peripheral") # Confirm BLE setup
ble_c.scan() # Scan for peripherals
print("Central scanning for 'Clu-Bots'") # Show scanning status
ble_c.connect(name='Clu-Bots') # Connect to peripheral by name
print("Central requested connect to 'Clu-Bots'") # Show connection attempt
last_app_msg = "" # Store last message from app
print("last_app_msg initialized as empty") # Confirm variable init
def handle_method(key1, key2, key3, keyx): # Define receive callback
msg = str(key1) # Convert main payload to text
print("APP->R32:", msg) # Show incoming app message
global last_app_msg # Declare global update
last_app_msg = msg # Save last app message
print("Stored last_app_msg:", last_app_msg) # Confirm storage
handle = ble_handle.Handle() # Create callback handler
handle.recv(handle_method) # Attach callback to receive BLE messages
print("BLE receive callback attached") # Confirm attachment
print("Type a PC message then press Enter") # Prompt PC user
pc_msg = input("PC->R32: ") # Read PC input line
print("PC->R32 received:", pc_msg) # Echo PC message
print("BRIDGE APP->PC:", last_app_msg if last_app_msg else "NO DATA") # Forward app data to PC
print("BRIDGE PC->APP:", pc_msg if pc_msg else "NO DATA") # Forward PC data (for app UI later)
shared_value = 0 # Shared numeric value across App and PC
print("shared_value starts at:", shared_value) # Show initial shared value
if last_app_msg.startswith("SET:"): # If app sent a set command
shared_value = int(last_app_msg.split(":")[1]) # Parse and set
print("SYNC from APP ->", shared_value) # Confirm sync
if pc_msg.startswith("SET:"): # If PC sent a set command
shared_value = int(pc_msg.split(":")[1]) # Parse and set
print("SYNC from PC ->", shared_value) # Confirm sync
priority = "APP" # Decide conflict resolution
print("Priority =", priority) # Show chosen priority
chosen_cmd = None # Placeholder for a final chosen command
print("chosen_cmd initialized as None") # Confirm init
if priority == "APP": # App wins conflicts
chosen_cmd = last_app_msg if last_app_msg else pc_msg # Prefer app
print("Chosen (APP priority):", chosen_cmd) # Show decision
else: # PC wins conflicts
chosen_cmd = pc_msg if pc_msg else last_app_msg # Prefer PC
print("Chosen (PC priority):", chosen_cmd) # Show decision
External explanation
- What it teaches: Your R32 becomes a bridge, keeping the App and PC in sync while accepting commands from both.
- Why it works: BLE callback captures App messages instantly; Serial input reads the PC message; simple labels and formats make forwarding and syncing reliable; a priority rule chooses the final command.
- Key concept: “Listen on both sides; forward and sync.”
Story time
You built a comms hub. Your robot translates between your phone and your computer like a friendly interpreter—fast, clear, and always on duty.
Debugging (2)
Debugging 5.6.A – App vs PC control conflicts
Problem: Both send different commands at the same time; behavior feels random.
Clues: Serial shows mixed lines; the robot flips between actions.
Broken code:
# No priority rule -> last write wins unpredictably
chosen = last_pc_msg # Always pick PC (or always App) without logic
Fixed code:
priority = "APP" # Decide who wins
if priority == "APP": # App preferred
chosen = last_app_msg if last_app_msg else last_pc_msg # Pick app else PC
else: # PC preferred
chosen = last_pc_msg if last_pc_msg else last_app_msg # Pick PC else app
print("Chosen command:", chosen) # Show final decision
Why it works: A clear priority rule prevents flip-flopping and inconsistent behavior.
Avoid next time: Write your rule first, code it second.
Debugging 5.6.B – Data desynchronization
Problem: App shows an old value, PC shows a new one.
Clues: Different “shared_value” printed in each path.
Broken code:
# Update only from PC, ignore the app
if pc_msg.startswith("SET:"):
shared_value = int(pc_msg.split(":")[1]) # Update
print("Shared:", shared_value) # Might not match the app's request
Fixed code:
# Update from both sides
if last_app_msg.startswith("SET:"): # Apply app update first
shared_value = int(last_app_msg.split(":")[1]) # Update
print("SYNC from APP ->", shared_value) # Confirm
if pc_msg.startswith("SET:"): # Then apply PC update
shared_value = int(pc_msg.split(":")[1]) # Update
print("SYNC from PC ->", shared_value) # Confirm
print("Shared (final):", shared_value) # Both sides consistent
Why it works: Applying updates from both sources ensures the final value is consistent.
Avoid next time: Use the same format and apply updates in a predictable order.
Final checklist
- App connects and sends a message
- PC types a line and the R32 echoes it
- “BRIDGE APP->PC” and “BRIDGE PC->APP” lines print clearly
- Shared value updates from both App and PC
- A clear priority rule chooses the final command
Extras
- 🧠 Student tip: Use “STATUS?” from the PC to trigger “STATUS:OK” back to both sides.
- 🧑🏫 Instructor tip: Have students design their own “KEY:VALUE” message set; consistency reduces bugs.
- 📖 Glossary:
- Peripheral: Device that advertises and accepts connections.
- Central: Device that scans and initiates connections.
- Bridge: A program that forwards messages between two sides.
- 💡 Mini tips:
- Keep messages short (e.g., CMD:FWD).
- Print labels so you can trace flows (APP->R32, PC->R32).
- If input blocks the loop, read once per cycle or use short prompts.