🌐 Level 6 – Wi-Fi Robotics

Project 6.8: "WiFi Messenger Robot"

 

What you’ll learn

  • ✅ WiFi messaging: Receive short text messages over WiFi and parse them into actions.
  • ✅ Navigation to destination: Move to predefined waypoints using simple steps.
  • ✅ Visual and audio delivery: Show messages on OLED and beep/LED when delivered.
  • ✅ Delivery confirmation: Send a reply back to the sender with status and time stamp.
  • ✅ Multi‑message queue: Handle multiple incoming messages in order without losing any.

Blocks glossary (used in this project)

  • WiFi STA + UDP/TCP: Connect to WiFi; use UDP for messages and TCP for optional status page.
  • Message parser: Read “TO:ROOM_A|MSG:Hello|ID:42” and split into fields.
  • Destinations table: Map labels like ROOM_A to coordinates or steps.
  • OLED display: Show sender, destination, and message lines.
  • LED/buzzer: Acknowledge delivery with a short pattern.
  • Serial println: Print “RX:…”, “NAV:…”, “DELIVER:…”, “CONFIRM:…”, and “QUEUE:…” lines.

What you need

PartHow many?Pin connection / Notes
D1 R32 (ESP32)1USB cable (30 cm)
WiFi network (2.4 GHz)1SSID and PASSWORD ready
OLED SSD1306 128×641I2C: SCL→22, SDA→21, VCC, GND
LED module1Signal → Pin 13, VCC, GND
Buzzer module1Signal → Pin 23, VCC, GND
Motors + L298N1 setLeft IN1→18, IN2→19; Right IN3→5, IN4→23

Notes

  • Share ground across R32, OLED, LED, buzzer, and L298N.
  • Keep OLED wires short; confirm I2C address (default 0x3C).
  • Use UDP for fast, simple messaging; reserve a fixed port for class.

Before you start

  • WiFi SSID/PASSWORD are correct
  • OLED wired to 22/21 and powers on
  • Serial monitor open and shows:
print("Ready!")  # Confirm serial is working so you can see messages

Microprojects 1–5

Microproject 6.8.1 – Receiving messages via WiFi

Goal: Join WiFi, open a UDP socket, receive a text message, and print it.
Blocks used:

  • WiFi STA: Connect and show IP.
  • UDP recvfrom: Read bytes and decode.

MicroPython code:

import network  # Import network to manage WiFi
import socket  # Import socket to handle UDP
import time  # Import time for short delays

SSID = "YourSSID"  # Put your WiFi SSID here
PASSWORD = "YourPassword"  # Put your WiFi password here
PORT_RX = 6200  # Choose a UDP port to receive messages

wlan = network.WLAN(network.STA_IF)  # Create a WiFi station interface
wlan.active(True)  # Activate WiFi hardware
wlan.connect(SSID, PASSWORD)  # Start connecting to WiFi
print("NET:CONNECTING", SSID)  # Print which network we are connecting to

while not wlan.isconnected():  # Wait until connected
    time.sleep(0.2)  # Small delay between checks
print("NET:CONNECTED", wlan.ifconfig()[0])  # Print assigned IP

u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # Create a UDP socket
u.bind(("0.0.0.0", PORT_RX))  # Bind to all interfaces on chosen port
u.settimeout(1.0)  # Set timeout so code does not block forever
print("UDP:LISTEN", PORT_RX)  # Print listening port

try:  # Try to receive one message
    data, addr = u.recvfrom(256)  # Receive up to 256 bytes
    text = data.decode()  # Decode bytes to text
    print("RX:FROM", addr[0])  # Print sender IP
    print("RX:TEXT", text)  # Print received text
except OSError:  # If timeout happened
    print("RX:NONE")  # No message arrived
u.close()  # Close UDP socket

Reflection: One message proves your robot can “hear” over WiFi.
Challenge:

  • Easy: Print message length (“LEN:n”).
  • Harder: Keep the socket open and receive up to 3 messages in a loop.

Microproject 6.8.2 – Navigation to a predefined destination

Goal: Map destination labels (ROOM_A/B) to simple movement steps and print progress.
Blocks used:

  • Dict table: label → sequence.
  • Motor helpers: forward, left, right, stop.

MicroPython code:

import machine  # Import machine to control motor pins
import time  # Import time for movement pulses

# Motor pins for L298N
L_IN1 = machine.Pin(18, machine.Pin.OUT)  # Left IN1
L_IN2 = machine.Pin(19, machine.Pin.OUT)  # Left IN2
R_IN3 = machine.Pin(5, machine.Pin.OUT)   # Right IN3
R_IN4 = machine.Pin(23, machine.Pin.OUT)  # Right IN4
print("MOTORS:READY 18/19 5/23")  # Confirm motor pins

def motors_stop():  # Stop both motors
    L_IN1.value(0)  # Left IN1 LOW
    L_IN2.value(0)  # Left IN2 LOW
    R_IN3.value(0)  # Right IN3 LOW
    R_IN4.value(0)  # Right IN4 LOW
    print("MOVE:STOP")  # Print stop

def forward(pulse=0.25):  # Move forward
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("MOVE:FWD", pulse)  # Print action
    time.sleep(pulse)  # Run duration
    motors_stop()  # Stop after pulse

def left(pulse=0.18):  # Turn left
    L_IN1.value(0)  # Left backward LOW
    L_IN2.value(1)  # Left backward HIGH
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("MOVE:LEFT", pulse)  # Print action
    time.sleep(pulse)  # Run duration
    motors_stop()  # Stop after pulse

def right(pulse=0.18):  # Turn right
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(0)  # Right backward LOW
    R_IN4.value(1)  # Right backward HIGH
    print("MOVE:RIGHT", pulse)  # Print action
    time.sleep(pulse)  # Run duration
    motors_stop()  # Stop after pulse

routes = {  # Define simple routes for destinations
    "ROOM_A": ["FWD", "LEFT", "FWD"],  # Path to ROOM_A
    "ROOM_B": ["FWD", "RIGHT", "FWD"]  # Path to ROOM_B
}
print("NAV:ROUTES", list(routes.keys()))  # Print available routes

def run_route(dest):  # Execute route steps for a destination
    seq = routes.get(dest, [])  # Get step sequence or empty
    print("NAV:DEST", dest)  # Print destination
    for step in seq:  # Iterate over steps
        if step == "FWD":  # If forward step
            forward(0.25)  # Move forward pulse
        elif step == "LEFT":  # If left step
            left(0.18)  # Turn left pulse
        elif step == "RIGHT":  # If right step
            right(0.18)  # Turn right pulse
        time.sleep(0.05)  # Small spacing between steps

run_route("ROOM_A")  # Demo route to ROOM_A

Reflection: Destinations feel real when steps are clear and labeled.
Challenge:

  • Easy: Add “ROOM_C” with two turns.
  • Harder: Insert “PAUSE” steps to simulate doors or obstacles.

Microproject 6.8.3 – Message delivery (visual/auditory)

Goal: Show message on OLED and play a short LED/buzzer pattern on delivery.
Blocks used:

  • OLED show: title + two lines.
  • LED/buzzer pattern: 3 short blinks/beeps.

MicroPython code:

import machine  # Import machine to access I2C and pins
import time  # Import time for pattern timing
import oled128x64  # Import OLED driver for SSD1306 128x64

# OLED I2C setup
i2c = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000)  # Create I2C bus
oled = oled128x64.OLED(i2c, address=0x3c, font_address=0x3A0000, types=0)  # Initialize OLED
print("OLED:READY 0x3C")  # Confirm OLED ready

# LED and buzzer pins
led = machine.Pin(13, machine.Pin.OUT)  # LED on Pin 13
buzzer = machine.Pin(23, machine.Pin.OUT)  # Buzzer on Pin 23
print("ALERT:LED=13 BUZZER=23")  # Confirm alert pins

def show_delivery(to_label, msg_text):  # Display delivery on OLED
    oled.clear()  # Clear screen
    oled.shows('WiFi Messenger', x=0, y=0, size=1, space=0, center=False)  # Title line
    oled.shows('TO:'+to_label, x=0, y=12, size=1, space=0, center=False)  # Destination line
    oled.shows('MSG:'+msg_text, x=0, y=24, size=1, space=0, center=False)  # Message line
    oled.show()  # Refresh OLED
    print("DELIVER:OLED", to_label)  # Print deliver OLED

def celebrate():  # LED/buzzer pattern for delivery
    for i in range(3):  # Repeat 3 times
        led.value(1)  # LED ON
        buzzer.value(1)  # Buzzer ON
        time.sleep(0.1)  # On duration
        led.value(0)  # LED OFF
        buzzer.value(0)  # Buzzer OFF
        time.sleep(0.1)  # Off duration
    print("DELIVER:CELEBRATE")  # Print celebration done

show_delivery("ROOM_A", "Hello!")  # Demo display
celebrate()  # Demo alert pattern

Reflection: A tiny show + celebrate moment turns robots into friendly couriers.
Challenge:

  • Easy: Add a quiet mode (LED only).
  • Harder: Show a 2‑line scroll when the message is longer than screen width.

Microproject 6.8.4 – Delivery confirmation

Goal: Send “CONFIRM:ID=42 STATUS=DELIVERED TIME=hh:mm:ss” back to sender via UDP.
Blocks used:

  • UDP sendto: Send confirmation to sender IP:PORT.
  • Time stamp: Compose a simple hh:mm:ss from ticks.

MicroPython code:

import socket  # Import socket to send UDP confirmation
import time  # Import time for timestamp

SENDER_IP = "192.168.1.100"  # Put the sender's IP here
SENDER_PORT = 6200  # Use the same port the sender listens on
msg_id = "42"  # Demo message ID

def hhmmss():  # Create a simple hh:mm:ss from ticks
    t = time.ticks_ms() // 1000  # Get seconds since boot
    h = (t // 3600) % 24  # Compute hours modulo 24
    m = (t % 3600) // 60  # Compute minutes
    s = t % 60  # Compute seconds
    return "{:02d}:{:02d}:{:02d}".format(h, m, s)  # Format hh:mm:ss

u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # Create UDP socket
confirm_text = "CONFIRM:ID=" + msg_id + " STATUS=DELIVERED TIME=" + hhmmss()  # Build confirm line
u.sendto(confirm_text.encode(), (SENDER_IP, SENDER_PORT))  # Send to sender IP:PORT
print("CONFIRM:SENT", confirm_text)  # Print confirmation text
u.close()  # Close UDP socket

Reflection: Confirmation closes the loop—sender knows the job is done.
Challenge:

  • Easy: Add “STATUS=FAILED” when route not found.
  • Harder: Include “TO:ROOM_A” and “MSG_LEN:n” in confirmation.

Microproject 6.8.5 – Multiple message system

Goal: Keep a small queue; process messages one by one with timeouts and clear prints.
Blocks used:

  • List queue: append on receive; pop from front when processing.
  • State print: show queue length and active ID.

MicroPython code:

import network  # Import network for WiFi (assume already connected)
import socket  # Import socket for UDP messaging
import time  # Import time for pacing

PORT_RX = 6200  # Port to receive messages
queue = []  # Initialize an empty message queue
active_id = ""  # Track current message ID
print("QUEUE:INIT")  # Print init line

u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # Create UDP socket
u.bind(("0.0.0.0", PORT_RX))  # Bind local receive port
u.settimeout(0.2)  # Short timeout for responsiveness
print("UDP:LISTEN", PORT_RX)  # Print listening port

def enqueue(text, addr):  # Add a message to the queue
    queue.append((text, addr))  # Append tuple (text, sender addr)
    print("QUEUE:ADD", len(queue))  # Print new queue length

def dequeue():  # Take next message from queue
    if queue:  # If queue not empty
        print("QUEUE:NEXT", len(queue))  # Print queue length
        return queue.pop(0)  # Pop front item
    return None  # Otherwise return None

# Try receiving messages for a short period
start = time.ticks_ms()  # Record start time
while time.ticks_diff(time.ticks_ms(), start) < 1500:  # Run ~1.5 s
    try:  # Try receive
        data, addr = u.recvfrom(256)  # Receive bytes
        text = data.decode()  # Decode to text
        print("RX:TEXT", text)  # Print text
        enqueue(text, addr)  # Add to queue
    except OSError:  # On timeout
        pass  # Continue loop

item = dequeue()  # Get next queued message
if item:  # If there is an item
    text, addr = item  # Unpack text and sender address
    # Extract simple ID field
    parts = text.split("|")  # Split text by '|'
    id_part = next((p for p in parts if p.startswith("ID:")), "ID:?")  # Find ID
    active_id = id_part.split(":", 1)[1] if ":" in id_part else "?"  # Extract ID value
    print("QUEUE:ACTIVE_ID", active_id)  # Print active message ID
else:  # If queue empty
    print("QUEUE:EMPTY")  # Print empty queue
u.close()  # Close UDP socket

Reflection: Queues keep things calm—no message is forgotten, and each gets full attention.
Challenge:

  • Easy: Limit queue size to 5 and drop oldest on overflow.
  • Harder: Add a retry counter per message for robust delivery.

Main project – WiFi messenger robot

Blocks steps (with glossary)

  • WiFi + UDP: Connect, listen on a port, and receive tagged messages.
  • Parse fields: TO, MSG, ID; validate destination exists.
  • Navigate: Run route steps to destination with prints and safe pauses.
  • Deliver: Show on OLED and celebrate with LED/buzzer pattern.
  • Confirm: Send confirmation back to sender with ID and time.
  • Queue: Process multiple messages in order.

MicroPython code (mirroring blocks)

# Project 6.8 – WiFi Messenger Robot

import network  # Import network to manage WiFi STA mode
import socket  # Import socket to send/receive UDP
import machine  # Import machine for OLED, pins, and motors
import time  # Import time for delays and timestamps
import oled128x64  # Import OLED driver for SSD1306 128x64

# WiFi credentials and ports
SSID = "YourSSID"  # Put your WiFi SSID here
PASSWORD = "YourPassword"  # Put your WiFi password here
PORT_RX = 6200  # UDP port to receive messages
SENDER_PORT = 6200  # Port to send confirmation (same as sender's listen)
print("NET:CONFIG RX_PORT", PORT_RX)  # Print network config

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

# LED and buzzer for delivery alerts
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("ALERT:PINS LED=13 BUZZER=23")  # Confirm alert pins

# Motors: L298N pins
L_IN1 = machine.Pin(18, machine.Pin.OUT)  # Left IN1
L_IN2 = machine.Pin(19, machine.Pin.OUT)  # Left IN2
R_IN3 = machine.Pin(5, machine.Pin.OUT)   # Right IN3
R_IN4 = machine.Pin(23, machine.Pin.OUT)  # Right IN4
print("MOTORS:PINS 18/19 5/23")  # Confirm motor pins

def motors_stop():  # Stop both motors
    L_IN1.value(0)  # Left IN1 LOW
    L_IN2.value(0)  # Left IN2 LOW
    R_IN3.value(0)  # Right IN3 LOW
    R_IN4.value(0)  # Right IN4 LOW
    print("MOVE:STOP")  # Print stop

def forward(pulse=0.25):  # Move forward for a short pulse
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("MOVE:FWD", pulse)  # Print forward pulse
    time.sleep(pulse)  # Run motors for pulse
    motors_stop()  # Stop motors

def left(pulse=0.18):  # Turn left in place
    L_IN1.value(0)  # Left backward LOW
    L_IN2.value(1)  # Left backward HIGH
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("MOVE:LEFT", pulse)  # Print left turn pulse
    time.sleep(pulse)  # Run motors for pulse
    motors_stop()  # Stop motors

def right(pulse=0.18):  # Turn right in place
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(0)  # Right backward LOW
    R_IN4.value(1)  # Right backward HIGH
    print("MOVE:RIGHT", pulse)  # Print right turn pulse
    time.sleep(pulse)  # Run motors for pulse
    motors_stop()  # Stop motors

def show_delivery(to_label, msg_text):  # Display message on OLED
    oled.clear()  # Clear display
    oled.shows('WiFi Messenger', x=0, y=0, size=1, space=0, center=False)  # Title text
    oled.shows('TO:'+to_label, x=0, y=12, size=1, space=0, center=False)  # Destination text
    oled.shows('MSG:'+msg_text, x=0, y=24, size=1, space=0, center=False)  # Message text
    oled.show()  # Refresh display
    print("DELIVER:OLED", to_label)  # Print OLED delivery

def celebrate():  # LED/buzzer pattern to acknowledge delivery
    for i in range(3):  # Repeat 3 blinks/beeps
        led.value(1)  # LED ON
        buzzer.value(1)  # Buzzer ON
        time.sleep(0.1)  # On duration
        led.value(0)  # LED OFF
        buzzer.value(0)  # Buzzer OFF
        time.sleep(0.1)  # Off duration
    print("DELIVER:CELEBRATE")  # Print completion of celebration

routes = {  # Predefined routes to destinations
    "ROOM_A": ["FWD", "LEFT", "FWD"],  # Route steps to ROOM_A
    "ROOM_B": ["FWD", "RIGHT", "FWD"],  # Route steps to ROOM_B
    "HOME":   ["RIGHT", "FWD"]  # Route steps back to HOME
}
print("NAV:ROUTES", list(routes.keys()))  # Print available destinations

def run_route(dest):  # Execute movement sequence to a destination
    seq = routes.get(dest, [])  # Get sequence or empty list
    print("NAV:DEST", dest)  # Print chosen destination
    for step in seq:  # Loop through each step
        if step == "FWD":  # If forward
            forward(0.25)  # Forward pulse
        elif step == "LEFT":  # If left
            left(0.18)  # Left pulse
        elif step == "RIGHT":  # If right
            right(0.18)  # Right pulse
        time.sleep(0.05)  # Small pause between steps

def hhmmss():  # Build a simple hh:mm:ss timestamp
    t = time.ticks_ms() // 1000  # Seconds since boot
    h = (t // 3600) % 24  # Hours modulo 24
    m = (t % 3600) // 60  # Minutes
    s = t % 60  # Seconds
    return "{:02d}:{:02d}:{:02d}".format(h, m, s)  # Format text

def parse_fields(text):  # Parse TO, MSG, ID fields from message
    fields = {"TO": "", "MSG": "", "ID": ""}  # Initialize fields dictionary
    for part in text.split("|"):  # Split by '|'
        if ":" in part:  # If key:value format
            k, v = part.split(":", 1)  # Split into key and value
            if k in fields:  # If known field
                fields[k] = v  # Store value
    print("PARSE:", fields)  # Print parsed fields
    return fields  # Return fields dict

# WiFi connect
wlan = network.WLAN(network.STA_IF)  # Create station interface
wlan.active(True)  # Activate WiFi
wlan.connect(SSID, PASSWORD)  # Connect to WiFi
print("NET:CONNECTING", SSID)  # Print SSID
while not wlan.isconnected():  # Wait until connected
    time.sleep(0.2)  # Short delay
print("NET:CONNECTED", wlan.ifconfig()[0])  # Print local IP

# UDP socket for receiving and confirming
u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # Create UDP socket
u.bind(("0.0.0.0", PORT_RX))  # Bind receive port
u.settimeout(0.2)  # Short timeout to keep loop responsive
print("UDP:LISTEN", PORT_RX)  # Print listening port

queue = []  # Initialize message queue

def enqueue(text, addr):  # Add message to queue
    queue.append((text, addr))  # Append tuple (text, sender addr)
    print("QUEUE:ADD", len(queue))  # Print queue length

def dequeue():  # Remove the first message from queue
    if queue:  # If queue not empty
        print("QUEUE:NEXT", len(queue))  # Print current queue length
        return queue.pop(0)  # Pop front
    return None  # Otherwise, no item

print("RUN:Messenger")  # Announce start
while True:  # Main messenger loop
    # Receive phase
    try:  # Try receive a message
        data, addr = u.recvfrom(256)  # Read up to 256 bytes
        text = data.decode()  # Decode bytes to text
        print("RX:FROM", addr[0])  # Print sender IP
        print("RX:TEXT", text)  # Print raw message
        enqueue(text, addr)  # Add to processing queue
    except OSError:  # Timeout means no message
        pass  # Continue loop

    # Process one queued message per cycle
    item = dequeue()  # Get next message
    if item:  # If there is a message
        text, addr = item  # Unpack message and sender
        fields = parse_fields(text)  # Parse TO, MSG, ID
        to_label = fields["TO"]  # Extract destination
        msg_text = fields["MSG"]  # Extract message text
        msg_id = fields["ID"]  # Extract message ID
        if to_label in routes:  # If destination known
            run_route(to_label)  # Navigate to destination
            show_delivery(to_label, msg_text)  # Show message on OLED
            celebrate()  # Blink/beep to acknowledge delivery
            confirm_text = "CONFIRM:ID=" + msg_id + " TO=" + to_label + " STATUS=DELIVERED TIME=" + hhmmss()  # Build confirmation line
            u.sendto(confirm_text.encode(), (addr[0], SENDER_PORT))  # Send confirmation to sender IP
            print("CONFIRM:SENT", confirm_text)  # Print confirmation
        else:  # Unknown destination
            err_text = "CONFIRM:ID=" + (msg_id or "?") + " STATUS=FAILED REASON=UNKNOWN_DEST TIME=" + hhmmss()  # Build failure confirm
            u.sendto(err_text.encode(), (addr[0], SENDER_PORT))  # Send failure confirmation
            print("CONFIRM:SENT", err_text)  # Print failure
    time.sleep(0.05)  # Short delay to keep loop responsive

External explanation

  • What it teaches: How to turn WiFi text messages into real actions: parse fields, move to a destination, show the message, celebrate, and confirm. You also kept a queue so multiple messages are handled calmly.
  • Why it works: UDP keeps messages light; clear tags make parsing easy; routes stay simple and reliable; OLED + alerts make delivery obvious; confirmations close the loop.
  • Key concept: “Receive → parse → go → show → confirm.”

Story time

A ping arrives: “TO:ROOM_A | MSG:You’ve got mail.” Your robot glides down the hall, lights up the OLED, beeps three times, and replies, “Delivered at 09:12:03.” It’s a tiny courier with manners.


Debugging (2)

Debugging 6.8.1 – Message not delivered

Problem: Robot receives text but doesn’t move or show.
Clues: “REASON=UNKNOWN_DEST” appears; routes missing the label.
Broken code:

routes = {"ROOM_A": ["FWD"], "ROOMB": ["FWD"]}  # Typo in ROOM_B

Fixed code:

routes = {"ROOM_A": ["FWD"], "ROOM_B": ["FWD"]}  # Correct label
print("DEBUG:ROUTES", list(routes.keys()))  # Verify labels

Why it works: Matching labels ensures parsing finds a valid destination and runs the route.
Avoid next time: Keep a destination glossary card and test each route once.

Debugging 6.8.2 – Navigation to the wrong destination

Problem: Robot runs the wrong route for the message.
Clues: Parsed fields show TO is empty or truncated.
Broken code:

for part in text.split("|"):
    k, v = part.split(":")  # Fails on messages with extra ':' in MSG

Fixed code:

k, v = part.split(":", 1)  # Split only on the first ':' to preserve message body
print("DEBUG:PARSE", k, v)  # Verify parsed pairs

Why it works: Splitting once avoids breaking MSG when the text contains ‘:’ characters.
Avoid next time: Always parse with cautious splits and validate fields before moving.


Final checklist

  • WiFi connects and prints local IP
  • UDP socket listens and receives text reliably
  • TO/MSG/ID fields parse correctly even with ‘:’ inside MSG
  • Robot runs the correct route steps and stops cleanly
  • OLED shows TO and MSG; LED/buzzer celebrate delivery
  • Confirmation UDP reply includes ID, TO, STATUS, and TIME
  • Queue processes multiple messages in order without blocking

Extras

  • 🧠 Student tip: Add “QUIET:ON/OFF” to switch buzzer off at night—keep LED only.
  • 🧑‍🏫 Instructor tip: Provide a class‑wide destination map (ROOM_A/B/C/HOME) so everyone’s routes match.
  • 📖 Glossary:
    • Tag: A short key that labels message parts (e.g., TO, MSG, ID).
    • Queue: A list that processes items in arrival order.
    • Confirmation: A return message that proves delivery happened.
  • 💡 Mini tips:
    • Keep messages short and consistent: “TO:X|MSG:Y|ID:Z”.
    • Use fixed ports and IP reservations to simplify classroom networking.
    • Log “CONFIRM:SENT” on the sender PC to verify the full loop.
On this page