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,iffor continuous listening and decisions.
What you need
| Part | How many? | Pin connection (R32) |
|---|---|---|
| D1 R32 | 1 | USB cable |
| Sound sensor (analog mic) | 1 | OUT → Pin 32 (ADC), VCC, GND |
| LED or motor (feedback/actuator) | 1 | LED → Pin 13 |
| Buzzer (optional) | 1 | MIDI → Pin 26 |
| Bluetooth (built‑in) | 1 | Internal |
- 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.