Skip to content

Instantly share code, notes, and snippets.

@hasumikin
Last active February 16, 2026 08:09
Show Gist options
  • Select an option

  • Save hasumikin/c550283d21291cf8498b3faa51e90df7 to your computer and use it in GitHub Desktop.

Select an option

Save hasumikin/c550283d21291cf8498b3faa51e90df7 to your computer and use it in GitHub Desktop.

※和文はページ下部にあります

PicoRuby Workshop 2025 - Buzzer & PWM

Previous sections:

  1. Setup + LED: https://tinyurl.com/picoruby-2025
  2. Microphone: https://tinyurl.com/picoruby-mic

In this section, we'll learn about PWM (Pulse Width Modulation) - a powerful technique that allows us to simulate analog signals using digital outputs. We'll use PWM to create sounds with a buzzer and control LED brightness smoothly.

What is PWM?

PWM (Pulse Width Modulation) is a technique used to control the amount of power delivered to a device by rapidly switching it on and off. Think of it like a very fast light dimmer switch.

PWM has two key parameters:

  • Frequency: How many times per second the signal switches (measured in Hz)
  • Duty Cycle: The percentage of time the signal is "high" vs "low" (0-100%)

Digital signals simulating analog: By rapidly switching a digital signal on and off, we can simulate analog behavior. The device "sees" an average voltage proportional to the duty cycle.

Buzzer Circuit

Components needed:

  • 1x Piezo buzzer or small speaker
  • Jumper wires

Wiring:

RP2[GP11] --- Resistor(1kΩ) --- Buzzer[Arbitrary pin]
RP2[GND]  --- Buzzer[Another pin]
connection_2

Creating Sounds

Let's start experimenting with PWM in IRB:

irb> require 'pwm'
irb> buzzer = PWM.new 11
irb> buzzer.frequency 440  # A4 note (440 Hz)
irb> buzzer.duty 50        # 50% duty cycle
irb> buzzer.duty 25         # 25% duty cycle
irb> buzzer.duty 0         # Turn off the sound

You should hear a tone! Let's try different frequencies:

irb> buzzer.duty 50        # 50% duty cycle
irb> buzzer.frequency 880  # A5 note (one octave higher)
irb> buzzer.frequency 220  # A3 note (one octave lower)
irb> buzzer.duty 0         # Turn off the sound

Musical Scale

Let's create the classic "Do-Re-Mi" scale. Save this as scale.rb:

require 'pwm'

buzzer = PWM.new(11)

# Musical notes frequencies (Hz)
notes = {
  'C'  => 261.63,  # Do
  'D'  => 293.66,  # Re
  'E'  => 329.63,  # Mi
  'F'  => 349.23,  # Fa
  'G'  => 392.00,  # Sol
  'A'  => 440.00,  # La
  'B'  => 493.88,  # Si
  'C2' => 523.25   # Do (one octave higher)
}

scale = %w(C D E F G A B C2)

puts "Playing Do-Re-Mi scale..."

scale.each do |note|
  puts "Playing: #{note} (#{notes[note]} Hz)"
  buzzer.frequency(notes[note])
  buzzer.duty(50)  # 50% duty cycle for clear tone
  sleep 0.5
end

buzzer.duty(0)  # Turn off buzzer
puts "Scale complete!"

Simple Melody

Let's play a simple melody. Save this as twinkle.rb:

require 'pwm'

buzzer = PWM.new(11)

# Note frequencies
notes = {
  'C' => 261.63, 'D' => 293.66, 'E' => 329.63,
  'F' => 349.23, 'G' => 392.00, 'A' => 440.00,
  'B' => 493.88, 'C2' => 523.25, 'REST' => 0
}

# Twinkle, Twinkle, Little Star
melody = %w(
  C C G G A A G REST
  F F E E D D C REST
)

puts "Playing 'Twinkle, Twinkle, Little Star'..."

melody.each_with_index do |note, i|
  buzzer.frequency notes[note]
  sleep_ms 200
  buzzer.frequency 0 # Staccato
  sleep_ms 200
end

buzzer.duty 0
puts "Melody complete!"

PWM for LED Brightness Control

PWM isn't just for sound - it's also perfect for controlling LED brightness! The human eye perceives the rapid on/off switching as variable brightness.

Additional Components:

  • 1x LED and 1x 1kΩ resistor (from previous sections)

Additional Wiring:

RP2[GP16] --- Resistor(1kΩ) --- LED[Anode(long leg)]
RP2[GND]  --- LED[Cathode(short leg)]

Let's create smooth LED fading. Save this as fade_led.rb:

require 'pwm'

led = PWM.new(16) # Not GPIO this time!
led.frequency(1000)  # 1kHz frequency for smooth dimming

puts "LED breathing effect - Press Ctrl+C to stop"

loop do
  # Fade in
  puts "Fade in..."
  0.upto(100) do |brightness|
    led.duty(brightness)
    sleep_ms 20  # 20ms delay for smooth transition
  end
  # Fade out
  puts "Fade out..."
  100.downto(0) do |brightness|
    led.duty(brightness)
    sleep_ms 20
  end
end

Advanced: Musical LED Visualizer

Let's combine sound and light! Save this as music_visualizer.rb:

require 'pwm'

buzzer = PWM.new(11)
led = PWM.new(16)

notes = {
  'C' => 261.63, 'D' => 293.66, 'E' => 329.63,
  'F' => 349.23, 'G' => 392.00, 'A' => 440.00,
  'B' => 493.88, 'C2' => 523.25
}

# Map frequency to LED brightness (higher pitch = brighter)
def frequency_to_brightness(freq)
  # Map 261Hz (C) to 523Hz (C2) → 20% to 100% brightness
  min_freq, max_freq = 261.63, 523.25
  min_bright, max_bright = 20, 100
  # Linear mapping
  brightness = min_bright + (freq - min_freq) * (max_bright - min_bright) / (max_freq - min_freq)
  [brightness, 100].min.to_i  # Cap at 100%
end

scale = %w(C D E F G A B C2)

puts "Musical LED Visualizer!"
puts "Higher notes = brighter LED"

led.frequency(2000)  # High frequency for smooth LED dimming

scale.each do |note|
  freq = notes[note]
  brightness = frequency_to_brightness(freq)

  puts "#{note}: #{freq}Hz → #{brightness}% brightness"

  # Play note and set LED brightness
  buzzer.frequency(freq)
  buzzer.duty(40)
  led.duty(brightness)

  sleep_ms 600
end

# Turn off both
buzzer.duty(0)
led.duty(0)
puts "Visualization complete!"

Sound-Reactive Buzzer

Let's combine our microphone knowledge with PWM to create a sound-reactive buzzer. Save this as sound_reactive.rb:

require 'pwm'
require 'adc'

buzzer = PWM.new(11)
led = PWM.new(16)
mic = ADC.new(26)

puts "Sound-reactive system started!"
puts "Make noise to trigger different tones"

led.frequency(1000)

loop do
  sound_level = mic.read_raw
  sound_intensity = (sound_level - 2048).abs # 0..2048

  if 500 < sound_intensity
    # Map sound intensity to frequency (100-2000 Hz)
    frequency = (sound_intensity - 500).clamp(100, 2000)
    brightness = ((sound_intensity - 500) / 20).clamp(0, 100)
    buzzer.frequency(frequency)
    buzzer.duty(30)
    led.duty(brightness)
    puts "Sound: #{sound_level}#{frequency}Hz, #{brightness}%"
  else
    buzzer.duty(0)
    led.duty(0)
  end

  sleep_ms 50  # 20 updates per second
end

Exercises

For those who want to explore further:

  1. Custom Melodies: Create your own melodies using the note frequency system. Try "Happy Birthday" or your favorite song's melody.

    ref: https://www.hoffmanacademy.com/store/sheet-music/happy-birthday-to-you

  2. Advanced PWM Patterns: Experiment with different duty cycles for the buzzer:

    # Try different duty cycles and observe sound quality
    [10, 25, 50, 75, 90].each do |duty|
      buzzer.duty(duty)
      sleep 1
    end
  3. LED Breathing with Math: Create more natural breathing patterns using sine waves:

    require 'pwm'
    
    led = PWM.new(16)
    led.frequency(2000)
    
    t = 0
    loop do
      brightness = (Math.sin(t) * 40 + 50).to_i  # 10-90% range
      led.duty(brightness)
      t += 0.1
      sleep_ms 50
    end
  4. RTTTL Parser: Research and implement a basic RTTTL (Ring Tone Text Transfer Language) parser to play cell phone ringtones:

    # Example RTTTL string for "Twinkle":
    RTTTL.play "Twinkle:d=4,o=5,b=120:c,c,g,g,a,a,2g,f,f,e,e,d,d,2c"

    d = default duration, o = octave, b = beats per minute

ブザー & PWM

前のセクション:

  1. Setup + LED: https://tinyurl.com/picoruby-2025
  2. Microphone: https://tinyurl.com/picoruby-mic

このセクションでは、PWM(Pulse Width Modulation) を学びます。PWM は、デジタル出力を使ってアナログ信号を擬似的に作れる強力な技術です。PWM を使ってブザーで音を作ったり、LED の明るさを滑らかに制御したりします。

PWM とは?

PWM(Pulse Width Modulation) は、信号を高速にオン・オフすることで、デバイスへ供給される電力の量を制御する技術です。とても高速な調光スイッチのようなものだと考えてください。

PWM には 2 つの重要なパラメータがあります。

  • Frequency: 1 秒あたりに信号が切り替わる回数(Hz)
  • Duty Cycle: 信号が "high" の時間と "low" の時間の割合(0〜100%)

デジタル信号でアナログを擬似的に表現する: デジタル信号を高速にオン・オフすることで、アナログ的な振る舞いを擬似的に作れます。デバイス側からは、デューティ比に比例した平均電圧が見えるようになります。

ブザー回路

必要な部品:

  • 1x 圧電ブザーまたは小型スピーカー
  • ジャンパーワイヤ

配線:

RP2[GP11] --- Resistor(1kΩ) --- Buzzer[Arbitrary pin]
RP2[GND]  --- Buzzer[Another pin]
connection_2

音を作る

IRB で PWM を試すところから始めましょう。

irb> require 'pwm'
irb> buzzer = PWM.new 11
irb> buzzer.frequency 440  # A4 note (440 Hz)
irb> buzzer.duty 50        # 50% duty cycle
irb> buzzer.duty 25         # 25% duty cycle
irb> buzzer.duty 0         # Turn off the sound

音が鳴るはずです。次は周波数を変えてみましょう。

irb> buzzer.duty 50        # 50% duty cycle
irb> buzzer.frequency 880  # A5 note (one octave higher)
irb> buzzer.frequency 220  # A3 note (one octave lower)
irb> buzzer.duty 0         # Turn off the sound

音階

定番の「ドレミ」音階を作ります。これを scale.rb として保存してください。

require 'pwm'

buzzer = PWM.new(11)

# Musical notes frequencies (Hz)
notes = {
  'C'  => 261.63,  # Do
  'D'  => 293.66,  # Re
  'E'  => 329.63,  # Mi
  'F'  => 349.23,  # Fa
  'G'  => 392.00,  # Sol
  'A'  => 440.00,  # La
  'B'  => 493.88,  # Si
  'C2' => 523.25   # Do (one octave higher)
}

scale = %w(C D E F G A B C2)

puts "Playing Do-Re-Mi scale..."

scale.each do |note|
  puts "Playing: #{note} (#{notes[note]} Hz)"
  buzzer.frequency(notes[note])
  buzzer.duty(50)  # 50% duty cycle for clear tone
  sleep 0.5
end

buzzer.duty(0)  # Turn off buzzer
puts "Scale complete!"

シンプルなメロディ

簡単なメロディを演奏します。これを twinkle.rb として保存してください。

require 'pwm'

buzzer = PWM.new(11)

# Note frequencies
notes = {
  'C' => 261.63, 'D' => 293.66, 'E' => 329.63,
  'F' => 349.23, 'G' => 392.00, 'A' => 440.00,
  'B' => 493.88, 'C2' => 523.25, 'REST' => 0
}

# Twinkle, Twinkle, Little Star
melody = %w(
  C C G G A A G REST
  F F E E D D C REST
)

puts "Playing 'Twinkle, Twinkle, Little Star'..."

melody.each_with_index do |note, i|
  buzzer.frequency notes[note]
  sleep_ms 200
  buzzer.frequency 0 # Staccato
  sleep_ms 200
end

buzzer.duty 0
puts "Melody complete!"

LED の明るさ制御のための PWM

PWM は音だけでなく、LED の明るさ制御にも最適です。高速なオン・オフの切り替えを、人間の目は明るさの違いとして知覚します。

追加の部品:

  • 1x LED と 1x 1kΩ 抵抗(前のセクションのもの)

追加の配線:

RP2[GP16] --- Resistor(1kΩ) --- LED[Anode(long leg)]
RP2[GND]  --- LED[Cathode(short leg)]

滑らかにフェードする LED を作ります。これを fade_led.rb として保存してください。

require 'pwm'

led = PWM.new(16) # Not GPIO this time!
led.frequency(1000)  # 1kHz frequency for smooth dimming

puts "LED breathing effect - Press Ctrl+C to stop"

loop do
  # Fade in
  puts "Fade in..."
  0.upto(100) do |brightness|
    led.duty(brightness)
    sleep_ms 20  # 20ms delay for smooth transition
  end
  # Fade out
  puts "Fade out..."
  100.downto(0) do |brightness|
    led.duty(brightness)
    sleep_ms 20
  end
end

応用: 音階に連動する LED ビジュアライザ

音と光を組み合わせましょう。これを music_visualizer.rb として保存してください。

require 'pwm'

buzzer = PWM.new(11)
led = PWM.new(16)

notes = {
  'C' => 261.63, 'D' => 293.66, 'E' => 329.63,
  'F' => 349.23, 'G' => 392.00, 'A' => 440.00,
  'B' => 493.88, 'C2' => 523.25
}

# Map frequency to LED brightness (higher pitch = brighter)
def frequency_to_brightness(freq)
  # Map 261Hz (C) to 523Hz (C2) → 20% to 100% brightness
  min_freq, max_freq = 261.63, 523.25
  min_bright, max_bright = 20, 100
  # Linear mapping
  brightness = min_bright + (freq - min_freq) * (max_bright - min_bright) / (max_freq - min_freq)
  [brightness, 100].min.to_i  # Cap at 100%
end

scale = %w(C D E F G A B C2)

puts "Musical LED Visualizer!"
puts "Higher notes = brighter LED"

led.frequency(2000)  # High frequency for smooth LED dimming

scale.each do |note|
  freq = notes[note]
  brightness = frequency_to_brightness(freq)

  puts "#{note}: #{freq}Hz → #{brightness}% brightness"

  # Play note and set LED brightness
  buzzer.frequency(freq)
  buzzer.duty(40)
  led.duty(brightness)

  sleep_ms 600
end

# Turn off both
buzzer.duty(0)
led.duty(0)
puts "Visualization complete!"

音に反応するブザー

マイクの知識と PWM を組み合わせて、音に反応するブザーを作ります。これを sound_reactive.rb として保存してください。

require 'pwm'
require 'adc'

buzzer = PWM.new(11)
led = PWM.new(16)
mic = ADC.new(26)

puts "Sound-reactive system started!"
puts "Make noise to trigger different tones"

led.frequency(1000)

loop do
  sound_level = mic.read_raw
  sound_intensity = (sound_level - 2048).abs # 0..2048

  if 500 < sound_intensity
    # Map sound intensity to frequency (100-2000 Hz)
    frequency = (sound_intensity - 500).clamp(100, 2000)
    brightness = ((sound_intensity - 500) / 20).clamp(0, 100)
    buzzer.frequency(frequency)
    buzzer.duty(30)
    led.duty(brightness)
    puts "Sound: #{sound_level}#{frequency}Hz, #{brightness}%"
  else
    buzzer.duty(0)
    led.duty(0)
  end

  sleep_ms 50  # 20 updates per second
end

演習

さらに探求したい方へ:

  1. カスタムメロディ: 音の周波数システムを使って、自分のメロディを作ってください。「Happy Birthday」や好きな曲のメロディを試してください。

    ref: https://www.hoffmanacademy.com/store/sheet-music/happy-birthday-to-you

  2. 高度な PWM パターン: ブザーの duty の値を変えて、違いを試してください。

    # Try different duty cycles and observe sound quality
    [10, 25, 50, 75, 90].each do |duty|
      buzzer.duty(duty)
      sleep 1
    end
  3. 数学で作る LED ブリージング: 正弦波を使って、より自然なブリージングパターンを作ってください。

    require 'pwm'
    
    led = PWM.new(16)
    led.frequency(2000)
    
    t = 0
    loop do
      brightness = (Math.sin(t) * 40 + 50).to_i  # 10-90% range
      led.duty(brightness)
      t += 0.1
      sleep_ms 50
    end
  4. RTTTL パーサ: 携帯電話の着信音を鳴らすために、基本的な RTTTL(Ring Tone Text Transfer Language)パーサを調べて実装してください。

    # Example RTTTL string for "Twinkle":
    RTTTL.play "Twinkle:d=4,o=5,b=120:c,c,g,g,a,a,2g,f,f,e,e,d,d,2c"

    d = default duration, o = octave, b = beats per minute

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment