📱 Level 5 – App Communication

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:

  1. Init central + peripheral (“Clu-Bots”).
  2. Scan and connect by name.
  3. Attach receive callback.
  4. 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:

  1. Print “Type something”.
  2. Read input().
  3. 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:

  1. Read app message into last_app_msg (already done by callback).
  2. Read PC message using input().
  3. 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:

  1. Parse app command like “SET:42”.
  2. PC can send “SET:99”.
  3. 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:

  1. Set priority = “APP”.
  2. Read PC input, check last_app_msg.
  3. Choose command based on priority.
  4. 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:

  1. Init BLE (central + peripheral “Clu-Bots”), scan, connect.
  2. Attach receive callback to update last_app_msg.
  3. Read PC input and echo it.
  4. Bridge both ways with clear labels.
  5. 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.
On this page