🤖 Level 4 – Mobile Robotics

Project 4.9: "Voice Control (Simulated)"

 

What you’ll learn

  • Goal 1: Detect sound levels using the official ADC block as a microphone input.
  • Goal 2: Treat sound spikes as commands (simulated “voice”) and map them to actions.
  • Goal 3: Recognize simple clap patterns by timing and thresholds.
  • Goal 4: Control robot actuators with sound commands and give audible feedback.
  • Goal 5: Integrate Bluetooth to report recognized commands to a phone/PC.

Blocks glossary

  • machine.ADC(machine.Pin(pin)).read(): Reads analog value (0–4095) from sound sensor.
  • adc.atten(…), adc.width(…): Configures ADC for stable microphone readings.
  • machine.Pin(pin, machine.Pin.OUT).value(v): Controls actuators like LEDs/motors.
  • music.MIDI(pin).play(…), .pitch_time(freq, ms): Emits tones for feedback.
  • ble_peripheral.BLESimplePeripheral(name), ble_handle.Handle().recv(cb): Sends/receives Bluetooth messages.
  • print(…), time.sleep_ms(ms): Logs events and paces loops for reliable detection.
  • control: while True, if for continuous listening and decisions.

What you need

PartHow many?Pin connection (R32)
D1 R321USB cable
Sound sensor (analog mic)1OUT → Pin 32 (ADC), VCC, GND
LED or motor (feedback/actuator)1LED → Pin 13
Buzzer (optional)1MIDI → Pin 26
Bluetooth (built‑in)1Internal
  • Share GND for all components.
  • Aim the microphone toward the sound source and keep wires short for cleaner signals.

Before you start

  • Connect the sound sensor OUT to ADC pin 32; connect LED to pin 13; optional buzzer to pin 26.
  • Open the Serial monitor to see logs.
  • Quick test:
print("Ready!")  # Serial: confirm the board and monitor are working

Microprojects 1–5

Microproject 4.9.1 – Sound detection as a command

import machine                                     # Load hardware library (ADC, Pin)
import time                                        # Load time library for pacing

adc32 = machine.ADC(machine.Pin(32))               # Create ADC on pin 32 for sound sensor
adc32.atten(machine.ADC.ATTN_11DB)                 # Set attenuation for wider voltage range
adc32.width(machine.ADC.WIDTH_12BIT)               # Set resolution to 12 bits (0–4095)
print("[Sound] ADC pin=32 ready")                  # Serial: confirm ADC setup

value = adc32.read()                               # Read the current sound level (0–4095)
print("[Sound] Level:", value)                     # Serial: show the sound level

TH_CMD = 2500                                      # Threshold for “command” spike (example)
if value >= TH_CMD:                                # If sound level crosses threshold
    print("CMD:VOICE")                             # Serial: simulate command recognized
else:                                              # Otherwise below threshold
    print("IDLE")                                  # Serial: idle (no command)
time.sleep_ms(300)                                 # Small delay to observe

Reflection: You turned a sound spike into a simple “voice” command.
Challenge: Try TH_CMD at 2200 and 2800 to find the best threshold for your room.


Microproject 4.9.2 – Commands by clapping pattern

import machine                                     # Load hardware library for ADC
import time                                        # Load time library

adc32 = machine.ADC(machine.Pin(32))               # ADC on pin 32
adc32.atten(machine.ADC.ATTN_11DB)                 # Wider voltage range
adc32.width(machine.ADC.WIDTH_12BIT)               # 12-bit resolution
print("[Clap] ADC 32 ready")                       # Serial: confirm ADC

TH_CLAP = 2600                                     # Threshold for detecting a clap
CLAP_WINDOW_MS = 600                               # Window to detect a second clap

# First clap check
v1 = adc32.read()                                  # Read sound level
print("[Clap] v1=", v1)                            # Serial: show level
if v1 >= TH_CLAP:                                  # If first clap detected
    print("CLAP:1")                                 # Serial: mark first clap
    time.sleep_ms(CLAP_WINDOW_MS)                  # Wait window for second clap (simple timing)
    v2 = adc32.read()                              # Read sound level again
    print("[Clap] v2=", v2)                        # Serial: show second level
    if v2 >= TH_CLAP:                              # If second clap detected within window
        print("CMD:DOUBLE_CLAP")                   # Serial: recognized double clap command
    else:                                          # If not enough sound for second clap
        print("CMD:SINGLE_CLAP")                   # Serial: recognized single clap command
else:                                              # If no clap detected
    print("CLAP:0")                                 # Serial: no clap
time.sleep_ms(300)                                 # Small delay

Reflection: You created a simple pattern detector with timing and thresholds.
Challenge: Reduce CLAP_WINDOW_MS to 400 ms and see if double clap is still comfortable.


Microproject 4.9.3 – Robot control by sound

import machine                                     # Load pins for actuators (LED)
import time                                        # Load time library
import music                                       # Load MIDI music for feedback
# Reuse ADC setup from previous microproject in your full program when integrating

led = machine.Pin(13, machine.Pin.OUT)             # LED on pin 13 for visible feedback
midi = music.MIDI(26)                              # MIDI on pin 26 for audible feedback
print("[Control] LED=13 MIDI=26 ready")            # Serial: confirm actuators

# Simulated command (replace with detection logic when integrating)
cmd_voice = True                                   # Pretend we recognized a voice command
print("[Control] Simulated CMD:VOICE=", cmd_voice) # Serial: show simulated state

if cmd_voice:                                      # If command recognized
    led.value(1)                                   # Turn LED ON (action)
    midi.pitch_time(880, 200)                      # Short tone to confirm
    print("[Control] ACTION:LED_ON + BEEP")        # Serial: action log
else:                                              # If no command
    led.value(0)                                   # Turn LED OFF
    print("[Control] ACTION:LED_OFF")              # Serial: action log
time.sleep_ms(300)                                 # Small delay

Reflection: You mapped a command to a clear action and feedback.
Challenge: Add a second action (e.g., LED OFF with a 440 Hz tone) for a different command.


Microproject 4.9.4 – Auditory command feedback

import music                                       # Load MIDI music library
import time                                        # Load time library

midi = music.MIDI(26)                              # MIDI buzzer on pin 26
print("[Feedback] MIDI=26 ready")                  # Serial: confirm MIDI

midi.play(midi.DADADADUM)                          # Play a recognizable feedback pattern
print("[Feedback] Pattern played")                 # Serial: confirm pattern
time.sleep_ms(400)                                 # Small pause after pattern

midi.pitch_time(660, 300)                          # Play a confirmation tone
print("[Feedback] Tone 660 Hz 300 ms")             # Serial: confirm tone
time.sleep_ms(300)                                 # Short delay

Reflection: Clear audio patterns make “voice” interactions feel responsive.
Challenge: Try 990 Hz for “error” and 523 Hz for “OK” to distinguish outcomes.


Microproject 4.9.5 – Integration with other control modes (Bluetooth status)

import ble_peripheral                              # Load Bluetooth peripheral library
import ble_handle                                  # Load Bluetooth handle library
import time                                        # Load time library

ble_p = ble_peripheral.BLESimplePeripheral("Voice-R32")  # Create BLE peripheral with name
print("[BLE] 'Voice-R32' ready")                   # Serial: confirm BLE advertising

h = ble_handle.Handle()                            # Create RX handle object
print("[BLE] RX handle ready")                     # Serial: confirm handle

def rx_method(msg):                                # Receive callback method
    s = str(msg)                                   # Ensure string
    print("[BLE] RX:", s)                          # Serial: show command
    # For this microproject, we only print; integrate actions in the main project later

h.recv(rx_method)                                  # Register RX callback
print("[BLE] RX callback registered")              # Serial: confirm

# Simulate sending recognized command status
ble_p.send("STATUS:CMD=VOICE")                     # Send status to PC/phone
print("[BLE] TX: STATUS:CMD=VOICE")                # Serial: mirror status
time.sleep_ms(500)                                 # Short delay

Reflection: Bluetooth messages keep operators informed of recognized commands.
Challenge: Send “STATUS:CMD=DOUBLE_CLAP” when you detect that pattern.


Main project

Simulated voice control via sound detection, clap patterns, robot actions, audio feedback, and Bluetooth status

  • Detect sound: ADC reads microphone level and decides if it’s a command.
  • Clap patterns: Simple timing identifies single vs double clap.
  • Actions: LED feedback and optional buzzer confirm commands.
  • Bluetooth: Transmit command status to PC/phone.
import machine                                     # Load ADC and Pin libraries
import music                                       # Load MIDI for audio feedback
import ble_peripheral                              # Load Bluetooth peripheral
import ble_handle                                  # Load Bluetooth handle for RX
import time                                        # Load time library

# --- ADC setup for sound sensor ---
adc32 = machine.ADC(machine.Pin(32))               # ADC on pin 32
adc32.atten(machine.ADC.ATTN_11DB)                 # Wide voltage range
adc32.width(machine.ADC.WIDTH_12BIT)               # 12-bit resolution
print("[Main] ADC32 ready")                        # Serial: confirm ADC

# --- Actuators for feedback ---
led = machine.Pin(13, machine.Pin.OUT)             # LED on pin 13
midi = music.MIDI(26)                              # MIDI buzzer on pin 26
print("[Main] LED=13 MIDI=26 ready")               # Serial: confirm actuators

# --- Bluetooth status ---
ble_p = ble_peripheral.BLESimplePeripheral("Voice-R32")  # BLE peripheral name
h = ble_handle.Handle()                            # RX handle
print("[Main] BLE 'Voice-R32' ready")              # Serial: confirm BLE

def rx_method(msg):                                # Receive callback (optional control)
    s = str(msg)                                   # Ensure string
    print("[Main] BLE RX:", s)                     # Serial: show received command
    # Extend here later (mix sound + remote commands)

h.recv(rx_method)                                  # Register RX callback
print("[Main] BLE RX registered")                  # Serial: confirm RX

# --- Thresholds and timing for patterns ---
TH_CMD = 2500                                      # Threshold for generic “voice” command
TH_CLAP = 2600                                     # Threshold for clap detection
CLAP_WINDOW_MS = 600                               # Window for second clap

# --- Continuous listening loop ---
while True:                                        # Main loop
    v = adc32.read()                               # Read sound level
    print("[Main] v=", v)                          # Serial: show level

    if v >= TH_CMD:                                # If generic command by sound level
        print("CMD:VOICE")                         # Serial: recognized voice
        led.value(1)                               # LED ON feedback
        midi.pitch_time(880, 200)                  # High tone confirmation
        ble_p.send("STATUS:CMD=VOICE")             # BLE status message
        print("[Main] TX STATUS:CMD=VOICE")        # Serial: mirror BLE
        time.sleep_ms(400)                         # Short pause after action

        # Clap pattern check immediately after generic command
        time.sleep_ms(CLAP_WINDOW_MS)              # Wait window for timing
        v2 = adc32.read()                          # Read again for second clap
        print("[Main] v2=", v2)                    # Serial: show second read
        if v2 >= TH_CLAP:                          # If second clap detected
            print("CMD:DOUBLE_CLAP")               # Serial: double clap command
            midi.pitch_time(660, 300)              # Confirmation tone
            ble_p.send("STATUS:CMD=DOUBLE_CLAP")   # BLE status message
            print("[Main] TX STATUS:CMD=DOUBLE_CLAP")  # Serial: mirror BLE
        else:                                      # If only single clap/command
            print("CMD:SINGLE_CLAP")               # Serial: single clap command
            midi.pitch_time(523, 300)              # Different tone
            ble_p.send("STATUS:CMD=SINGLE_CLAP")   # BLE status message
            print("[Main] TX STATUS:CMD=SINGLE_CLAP")  # Serial: mirror BLE

        led.value(0)                               # Turn LED OFF after feedback
    else:                                          # If below command threshold
        print("IDLE")                              # Serial: idle
        time.sleep_ms(250)                         # Short idle delay

External explanation

We simulate “voice control” by treating sound spikes as commands using the ADC block. A timing window distinguishes single vs double claps without complex data structures. Actions are kept simple and visible (LED + tones), and Bluetooth sends short status messages so an operator can follow along.


Story time

Your robot can “hear” the room. Loud sounds feel like commands: a single clap says “go,” a double clap says “do more.” It answers back with lights, beeps, and a quiet message to your phone.


Debugging (2)

Debugging 4.9.A – Unrecognized commands

# Raise or lower thresholds until recognition is reliable
print("[Debug] Tune TH_CMD and TH_CLAP for your room")  # Serial: guidance
# Keep a steady cadence; avoid constant prints between reads

Debugging 4.9.B – Activation by ambient noise

# If background noise triggers commands, add a short confirmation delay
print("[Debug] Add 200 ms confirmation before acting")   # Serial: tip
# Example:
# time.sleep_ms(200)
# if adc32.read() >= TH_CMD: print("CMD:VOICE")          # Act only if still loud

Final checklist

  • ADC reads sound levels from Pin 32 consistently.
  • Thresholds detect generic “voice” and clap patterns.
  • LED and tones provide clear feedback for each command.
  • Bluetooth sends concise STATUS messages for recognized commands.
  • Loop cadence keeps behavior calm and readable.

Extras

  • Student tip: Choose distinct tones for different commands so they’re easy to memorize.
  • Instructor tip: Have students test thresholds in quiet vs noisy rooms and justify settings.
  • Glossary:
    • ADC: Analog‑to‑digital conversion for microphone voltage.
    • Threshold: The level at which detection flips to “command”.
    • Cadence: The rhythm of reads and actions in the loop.
  • Mini tips:
    • Keep wires short for the mic; avoid fans or direct wind noise.
    • Use short Serial messages to avoid flooding.
    • Pace detection with brief sleeps for stability.
On this page