Project 5.5: "Interactive Sound Sensor"
What you’ll learn
- ✅ Detect sound: Read a sound switch module (HIGH when loud, LOW when quiet).
- ✅ Trigger actions: Turn an LED on when a clap is detected.
- ✅ Visualize level: Show a simple sound bar on the 0.96″ OLED.
- ✅ Play a game: Build a clap reaction mini‑game.
- ✅ Control by sound: Map 1–2 claps to robot commands.
Key ideas
- Short definition: A sound switch gives HIGH when noise crosses its sensitivity setting.
- Real-world link: Smart lights and voice devices react to claps and loud sounds.
Blocks glossary (used in this project)
- Digital input (Pin): Reads HIGH/LOW from the sound sensor’s signal pin.
- Digital output (Pin): Turns an LED ON/OFF to show reactions.
- Delay ms / seconds: Waits between checks or animations.
- Variables: Store counts, timestamps, and states.
- OLED shows/rect/line: Draws text and bars to visualize sound activity.
- Serial println: Prints clear messages so you see what’s happening.
What you need
| Part | How many? | Pin connection |
|---|---|---|
| D1 R32 | 1 | USB cable (30 cm) |
| Sound sensor (digital) | 1 | Signal → Pin 34, VCC, GND |
| 10mm LED module (indicator) | 1 | Signal → Pin 13, VCC, GND |
| 0.96″ OLED (128×64) SSD1306 | 1 | I2C: SCL → Pin 22, SDA → Pin 21, VCC, GND |
🔌 Wiring tip: Pins 34–39 are input‑only; use Pin 34 for the sensor signal. Use Pin 13 for the LED.
📍 Pin map snapshot: Using pins 34 (sensor), 13 (LED), 22/21 (OLED). Other pins stay free.
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.5.1 – Sound level detection
Goal: Read the sound sensor’s HIGH/LOW and print it.
Blocks used:
- Digital input: Get Pin 34 value.
- Serial println: Print “SOUND:1/0”.
Block sequence:
- Set Pin 34 as input.
- Read value.
- Print and repeat.
MicroPython code:
import machine # Import machine to access Pin
import time # Import time to add small delays
sound = machine.Pin(34, machine.Pin.IN) # Create digital input on Pin 34 for sound sensor
print("Microproject 5.5.1: Sound sensor ready on Pin 34") # Status message
while True: # Continuous detection loop
val = sound.value() # Read HIGH(1) when loud, LOW(0) when quiet
print("SOUND:", val) # Show the current sound state
time.sleep(0.1) # Small delay to make prints readable
Reflection: HIGH means “noise detected”—try clapping and watch the serial output.
Challenge:
- Easy: Change delay to 50 ms for faster checks.
- Harder: Count detections over 1 second and print the total.
Microproject 5.5.2 – Activation of actions by sound
Goal: Turn an LED ON for half a second when noise is detected.
Blocks used:
- Digital input: Read Pin 34.
- Digital output: Control LED on Pin 13.
Block sequence:
- Create LED on Pin 13.
- If sound HIGH → LED ON.
- Delay 500 ms → LED OFF.
- Print status.
MicroPython code:
import machine # Import machine for Pin control
import time # Import time for delays
sound = machine.Pin(34, machine.Pin.IN) # Digital input for sound sensor
led = machine.Pin(13, machine.Pin.OUT) # Digital output for LED indicator
print("Microproject 5.5.2: LED on Pin 13, sound on Pin 34") # Status message
while True: # Loop forever
if sound.value() == 1: # Check if loud sound is detected
led.value(1) # Turn LED ON to indicate detection
print("LED: ON (sound detected)") # Explain the action
time.sleep(0.5) # Keep LED ON briefly (500 ms)
led.value(0) # Turn LED OFF
print("LED: OFF") # Explain the action
else: # No sound detected
led.value(0) # Ensure LED stays OFF
print("Quiet...") # Status message for quiet state
time.sleep(0.2) # Short pause before next check
Reflection: Feedback lights make it obvious that your robot is “listening.”
Challenge:
- Easy: Change the ON time to 200 ms.
- Harder: Add a cooldown: ignore new claps for 300 ms after turning off.
Microproject 5.5.3 – OLED sound level meter
Goal: Show a bar that grows with activity (claps per second).
Blocks used:
- I2C setup: Create i2c_extend.
- OLED shows/rect/line: Draw label and bar.
- Loop + math: Count detections for 1 second.
Block sequence:
- Setup I2C and OLED.
- Count sound=1 samples for 1 second.
- Map count to 0–100 pixels.
- Draw outline + filled bar.
- Show updates each second.
MicroPython code:
import machine # Import machine for I2C and Pin
import oled128x64 # Import SSD1306 OLED driver
import time # Import time for timing
sound = machine.Pin(34, machine.Pin.IN) # Digital input for sound sensor
i2c_extend = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000) # I2C on pins 22/21
oled = oled128x64.OLED(i2c_extend, address=0x3c, font_address=0x3A0000, types=0) # Initialize OLED
print("Microproject 5.5.3: OLED ready, counting claps per second") # Status message
while True: # Continuous meter loop
start = time.ticks_ms() # Record start time in milliseconds
hits = 0 # Reset the detection count
while time.ticks_diff(time.ticks_ms(), start) < 1000: # Count for 1000 ms
hits += sound.value() # Add 1 whenever sensor reads HIGH
time.sleep(0.02) # Sample every 20 ms
bar_width = min(hits, 100) # Limit bar width to max 100 pixels
print("Hits in 1s:", hits, "-> bar width:", bar_width) # Show count and width
oled.clear() # Clear the display for a fresh frame
print("Cleared OLED frame") # Confirm clear
oled.shows('SOUND:', x=0, y=0, size=1, space=0, center=False) # Draw label
print("Label 'SOUND:' drawn") # Confirm label
oled.rect(0, 16, 100, 12, 1) # Draw outline rectangle for the bar
print("Outline rect at (0,16) size (100x12)") # Confirm outline
for x in range(1, bar_width): # Fill the bar with vertical lines
oled.line(x, 17, x, 27, 1) # Draw line inside the outline
print("Filled bar to width:", bar_width) # Confirm fill
oled.show() # Refresh the OLED to show the frame
print("OLED show() called") # Confirm refresh
Reflection: Counting short “HIGH” samples turns claps into a simple visual level.
Challenge:
- Easy: Change sampling to every 10 ms.
- Harder: Add a second bar for “max hits” seen so far.
Microproject 5.5.4 – Sound reaction game
Goal: Clap to start, clap again as fast as possible—print your reaction time.
Blocks used:
- Digital input: Detect claps.
- Timing: Measure milliseconds.
- Serial println: Show the score.
Block sequence:
- Wait for first clap (start).
- Start a timer.
- Wait for second clap (stop).
- Print reaction time.
MicroPython code:
import machine # Import machine for Pin
import time # Import time for timing
sound = machine.Pin(34, machine.Pin.IN) # Sound sensor input on Pin 34
print("Microproject 5.5.4: Reaction game ready") # Status message
while True: # Loop to play repeatedly
print("Clap to START!") # Prompt the player
while sound.value() == 0: # Wait until a clap is detected
time.sleep(0.01) # Small wait to reduce CPU usage
t_start = time.ticks_ms() # Record start time
print("Started! Clap again!") # Prompt for the second clap
while sound.value() == 0: # Wait for the second clap
time.sleep(0.01) # Small wait
t_end = time.ticks_ms() # Record end time
reaction = time.ticks_diff(t_end, t_start) # Compute reaction time in ms
print("Reaction time (ms):", reaction) # Show the player's score
time.sleep(1) # Brief pause before the next round
Reflection: Your ears and timing matter—practice to reduce your reaction time.
Challenge:
- Easy: Add “BEST:” to track the lowest time.
- Harder: Add a random wait (1–3 s) before “Clap again!” to prevent guessing.
Microproject 5.5.5 – Robot control by sound
Goal: Use clap counts to print commands: 1 clap = FWD, 2 claps = STOP.
Blocks used:
- Digital input: Detect claps.
- Window timing: Count claps within 800 ms.
- Serial println: Print command words.
Block sequence:
- When first clap arrives, start a window.
- Count claps for 800 ms.
- If count==1 → FWD; if count>=2 → STOP.
- Print the command.
MicroPython code:
import machine # Import machine for Pin
import time # Import time for timing
sound = machine.Pin(34, machine.Pin.IN) # Sound sensor input on Pin 34
print("Microproject 5.5.5: Clap control (1=FWD, 2=STOP)") # Status message
while True: # Continuous command loop
if sound.value() == 1: # Detect first clap
window_start = time.ticks_ms() # Start counting window
claps = 1 # First clap counted
print("Clap detected, counting window started") # Status message
time.sleep(0.15) # Debounce to ignore sensor ringing
while time.ticks_diff(time.ticks_ms(), window_start) < 800: # 800 ms window
if sound.value() == 1: # New clap inside the window
claps += 1 # Increase clap count
print("Extra clap! Count:", claps) # Show updated count
time.sleep(0.15) # Debounce after each detection
if claps == 1: # Decide command for one clap
print("CMD:FWD") # Forward command (to be mapped to motors later)
else: # Two or more claps
print("CMD:STOP") # Stop command
time.sleep(0.3) # Small pause after command
else: # No clap yet
print("Listening...") # Show that we are waiting
time.sleep(0.1) # Short delay before next check
Reflection: Grouping claps into a short window lets you map patterns to actions.
Challenge:
- Easy: Add “CMD:LEFT” for 3 claps.
- Harder: Add a “MODE:SILENT” toggle to pause detection.
Main project – Interactive sound sensor
Blocks steps (with glossary)
- Digital input (Pin 34): Read HIGH/LOW from the sound sensor.
- Digital output (Pin 13): Flash LED on detection.
- I2C OLED (22/21): Draw a sound activity bar.
- Timing windows: Count claps for control.
- Serial prints: Show “SOUND”, “CMD”, and messages for the game.
Block sequence:
- Setup sound input on Pin 34 and LED on Pin 13.
- Detect sound → flash LED and print.
- Count activity per second → draw OLED bar.
- Reaction game: measure time between claps.
- Clap control: 1=FWD, 2=STOP (print commands).
MicroPython code (mirroring blocks)
# Project 5.5 – Interactive Sound Sensor
import machine # Access Pin and I2C hardware
import oled128x64 # SSD1306 OLED driver for 128x64 display
import time # Provide delays and timing utilities
sound = machine.Pin(34, machine.Pin.IN) # Sound sensor input on Pin 34
led = machine.Pin(13, machine.Pin.OUT) # LED indicator output on Pin 13
print("Inputs ready: sound=Pin34, LED=Pin13") # Confirm pin setup
i2c_extend = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000) # I2C bus on pins 22/21
oled = oled128x64.OLED(i2c_extend, address=0x3c, font_address=0x3A0000, types=0) # Initialize the OLED display
print("OLED initialized at address 0x3C") # Confirm display setup
best_reaction = None # Track the best (lowest) reaction time
print("Best reaction = None (no record yet)") # Explain initial state
while True: # Main loop to combine detection, visuals, and commands
# Part 1: Immediate feedback LED
if sound.value() == 1: # If a loud sound is detected
led.value(1) # Turn LED ON to acknowledge
print("SOUND:1 -> LED ON") # Show detection
time.sleep(0.2) # Keep LED ON briefly
led.value(0) # Turn LED OFF
print("LED OFF") # Confirm LED off
else: # No sound detected
print("SOUND:0") # Show quiet state
# Part 2: One-second activity bar on OLED
start = time.ticks_ms() # Start timing window
hits = 0 # Reset activity counter
while time.ticks_diff(time.ticks_ms(), start) < 1000: # Count for 1 second
hits += sound.value() # Add 1 whenever sensor is HIGH
time.sleep(0.02) # Sample at 20 ms intervals
bar_width = min(hits, 100) # Cap bar width at 100 pixels
print("Activity hits:", hits, "-> bar width:", bar_width) # Show activity
oled.clear() # Clear the OLED before drawing
print("OLED cleared") # Confirm clear
oled.shows('SOUND METER', x=0, y=0, size=1, space=0, center=False) # Title
print("Title drawn") # Confirm title
oled.rect(0, 16, 100, 12, 1) # Draw outline for the bar
print("Outline rect drawn") # Confirm outline
for x in range(1, bar_width): # Fill the bar with lines
oled.line(x, 17, x, 27, 1) # Draw one vertical line inside the outline
print("Bar filled to width:", bar_width) # Confirm bar fill
oled.show() # Update the OLED to show the meter
print("OLED frame shown") # Confirm display update
# Part 3: Quick clap control (1=FWD, 2=STOP)
if sound.value() == 1: # Detect a clap to start a command window
window_start = time.ticks_ms() # Record the window start time
claps = 1 # Count the initial clap
print("Command window started, claps=1") # Status message
time.sleep(0.15) # Debounce the sensor ringing
while time.ticks_diff(time.ticks_ms(), window_start) < 800: # 800 ms window for extra claps
if sound.value() == 1: # Additional clap detected
claps += 1 # Increase clap count
print("Extra clap detected, claps=", claps) # Show updated count
time.sleep(0.15) # Debounce to avoid double counting
if claps == 1: # Decide command for single clap
print("CMD:FWD") # Forward (to be mapped to motors later)
else: # Two or more claps
print("CMD:STOP") # Stop command
# Part 4: Reaction game (optional each loop)
print("Reaction game: clap to START") # Prompt player
while sound.value() == 0: # Wait for the first clap
time.sleep(0.01) # Small wait
t_start = time.ticks_ms() # Record start time
print("Started! Clap again!") # Prompt for second clap
while sound.value() == 0: # Wait for second clap
time.sleep(0.01) # Small wait
t_end = time.ticks_ms() # Record end time
reaction = time.ticks_diff(t_end, t_start) # Compute reaction time
print("Reaction (ms):", reaction) # Show score
if (best_reaction is None) or (reaction < best_reaction): # Check for new record
best_reaction = reaction # Update best score
print("BEST:", best_reaction) # Celebrate new record
else: # No improvement
print("Best stays at:", best_reaction) # Show current best
time.sleep(0.5) # Short pause before next overall cycle
External explanation
- What it teaches: You detected sound, reacted with an LED, visualized activity on an OLED, played a timing game, and mapped claps to commands.
- Why it works: The sensor outputs HIGH when sound crosses a threshold; code counts those highs, draws bars, and uses short time windows to interpret patterns (1 clap vs 2 claps).
- Key concept: “Detect → show → decide.”
Story time
Your robot now “hears” you. A single clap sends it forward; two claps stop it. The OLED becomes its little ear monitor.
Debugging (2)
Debugging 5.5.A – Sensor does not detect low sounds
Problem: Only very loud claps are recognized.
Clues: Serial mostly prints “SOUND:0” even when speaking or tapping.
Broken code:
# No debounce or sampling window; brief signals get missed
val = sound.value() # Single check may miss short pulses
Fixed code:
hits = 0 # Start a short sampling window
start = time.ticks_ms() # Record start time
while time.ticks_diff(time.ticks_ms(), start) < 200: # Sample for 200 ms
hits += sound.value() # Count brief highs
time.sleep(0.01) # Sample every 10 ms
print("Window hits:", hits) # See if low sounds register
Why it works: Sampling over a short window catches quick, low‑level pulses better than a single read.
Avoid next time: Use small windows and debouncing when reading brief signals.
Debugging 5.5.B – Activation by background noise
Problem: LED and commands trigger even in a noisy room.
Clues: Serial shows frequent “SOUND:1” without intentional claps.
Broken code:
if sound.value() == 1: # Immediate trigger on any HIGH
led.value(1) # LED ON
Fixed code:
if sound.value() == 1: # First HIGH detected
time.sleep(0.1) # Debounce to ignore brief noise
if sound.value() == 1: # Confirm still HIGH after 100 ms
led.value(1) # LED ON (confirmed event)
print("Confirmed clap -> LED ON") # Explain confirmation
Why it works: Debounce and confirmation reduce false positives from random noise.
Avoid next time: Confirm events and add a short cooldown after detection.
Final checklist
- I saw “SOUND:1/0” change with claps
- The LED flashed when sound was detected
- The OLED bar updated once per second
- Clap commands printed “CMD:FWD” or “CMD:STOP”
Extras
- 🧠 Student tip: Add “CMD:LEFT” for 3 claps and “CMD:RIGHT” for 4 claps.
- 🧑🏫 Instructor tip: Practice clap distance and sensitivity; show students how debounce prevents false triggers.
- 📖 Glossary:
- Debounce: A short pause to avoid counting the same event multiple times.
- Sampling window: A time box where you collect multiple readings.
- Activity bar: A simple visual showing “how much” noise happened.
- 💡 Mini tips:
- Keep sensor away from fan/buzzer to reduce false triggers.
- Use pins 34–39 for inputs; they’re stable for sensors.
- If the OLED shows nothing, check I2C address 0x3C and wiring.