📱 Level 5 – App Communication

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

PartHow many?Pin connection
D1 R321USB cable (30 cm)
Sound sensor (digital)1Signal → Pin 34, VCC, GND
10mm LED module (indicator)1Signal → Pin 13, VCC, GND
0.96″ OLED (128×64) SSD13061I2C: 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:

  1. Set Pin 34 as input.
  2. Read value.
  3. 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:

  1. Create LED on Pin 13.
  2. If sound HIGH → LED ON.
  3. Delay 500 ms → LED OFF.
  4. 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:

  1. Setup I2C and OLED.
  2. Count sound=1 samples for 1 second.
  3. Map count to 0–100 pixels.
  4. Draw outline + filled bar.
  5. 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:

  1. Wait for first clap (start).
  2. Start a timer.
  3. Wait for second clap (stop).
  4. 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:

  1. When first clap arrives, start a window.
  2. Count claps for 800 ms.
  3. If count==1 → FWD; if count>=2 → STOP.
  4. 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:

  1. Setup sound input on Pin 34 and LED on Pin 13.
  2. Detect sound → flash LED and print.
  3. Count activity per second → draw OLED bar.
  4. Reaction game: measure time between claps.
  5. 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.
On this page