🐝

ESP32でPWM (圧電スピーカ編)

2023/01/11に公開


はじめに

この記事では ESP32開発ボード(ESP32-WROVER-X/ESP32-WROOM-X/etc)を使用し、PWMにより圧電スピーカ(ピエゾスピーカ)を駆動して特定の周波数の音を出したり、音階や楽曲を演奏する方法について紹介します。
使用する言語はMicropythonです。

In English

This article describes how to drive a piezoelectric speaker with PWM connected to ESP32 using Micropython in Japanese.

圧電スピーカとは

圧電スピーカはピエゾスピーカともよばれ、ON/OFF(例えば3.3Vと0V)を繰り返す信号を与えるとその周波数の音が出力されます。単に電圧(たとえば3.3V)を印加しただけでは音は出ません。
よく似た物に圧電ブザーというのがあります。これは電圧を印加するだけで特定の周波数の音が出ます。部品を購入する際に間違えないようにして下さい。

圧電スピーカの動作原理については検索サイトで検索してみて下さい。

PWMとは

Pulse Width Modulation(パルス幅変調)の略で、下のアニメーションGIFのようにONとOFFの間隔を制御することによって出力電圧を変えることができます。

下の図では、ONの時間が長いとLEDは明るく光り、短いと暗くなります。

1周期の中でONになっている比率をデューティー比と言います。

PWMを使用することにより、モータの回転数制御やスピーカの音階制御ができます。
alt PWM
画像引用元:https://exploreembedded.com/wiki/images/5/54/0_LPC1768_PWM.gif

下の図のように、デューティー比を変化させることにより、実効電圧が変化します。
altDuty
画像引用元: https://dotstud.io/img/docs/pulse-width-modulation/2.png

周波数と周期の関係

下の図のように、同じ波形を繰り返す時間を周期と言い、T で表します。
周期 T と周波数 f は次の関係があります。周期と周波数は逆数の関係にあります。

f = {1 \over T}

例えば、周期が20[ms]の周波数は50[Hz]となります。

f = {1 \over 20 \times 10^{-3}} = 50 [Hz]

alt ここに画像が表示されない場合は下のリンクをクリックしてみて下さい。
画像引用元: http://www.miyazaki-gijutsu.com/series4/densi0221/fg2_2_6.gif

音階と周波数

ピアノ鍵盤のほぼ中央に位置する(左から4番目の)(A4)の音を440Hzとするよう国際標準化機構(ISO)で決められています

周波数が2倍になると、1オクターブ上の音になります。1オクターブの間に12種類の音があるので、音階の周波数は次のように、初項440、公比2の等比数列で表すことができます。

f = 440 \times 2^{n \over 12}

nは440から数えて何番目の音になるかという数値です。
Pythonで計算させると次のようになります。

base = 440

for n in range(16):
    f = int(base * 2**(n/12))
    print(n, f)

実行結果

0 440
1 466
2 493
3 523
4 554
5 587
6 622
7 659
8 698
9 739
10 783
11 830
12 880
13 932
14 987
15 1046

440Hzがラ、466Hzがラ#、493Hzがシ、523Hzがド...となり、880Hzが1オクターブ高いラ、1046Hzが1オクターブ高いドとなります。

440Hzより低い音はfor文のrange関数を、例えば、次のように変更すれば計算されます。

for n in range(-5, 16):

実行結果

-5 329
-4 349
-3 369
-2 391
-1 415
0 440
1 466
2 493
3 523
4 554
5 587
6 622
7 659
8 698
9 739
10 783
11 830
12 880
13 932
14 987
15 1046

Googleスプレッドシートにもまとめてみましたので、参照して下さい。
https://docs.google.com/spreadsheets/d/1djRqNPSsHJjjIkifF78_cVZ9MauO_0sOwQcWDFWFD4E/edit?usp=sharing

結線

ブレッドボード上に圧電スピーカを配置し、電極表示にしたがってGPIO26(圧電スピーカの+と表示された電極)とGND(圧電スピーカの何も表示のない電極)に接続します。
実際に使用する圧電スピーカの形状は下の図と異なる場合があります。

altブレッドボード図

結線例

LEDスイッチの結線も残っているのでゴチャゴチャしていますが、LEDの手前にある直径1cm程度の円筒形の部品が圧電スピーカです。+の端子から黄色のリード線でGPIO26に接続されています。片側の端子は黒色のリード線でGNDに接続されています。
alt結線例

MicropythonのPWMクラス

MicropythonのPWMクラスについての詳細は次のリンクを参照して下さい。
https://micropython-docs-ja.readthedocs.io/ja/latest/library/machine.PWM.html?highlight=pwm#limitations-of-pwm

サンプルコード1

周波数1kHzの音を1秒間、圧電スピーカから出力します。
説明用に行番号が入った画像ファイルを貼り付けていますが、コピペが必要な場合は、下のアコーディオンを展開して下さい。
alt説明用コード

コピペ用コード
pwm_1kHz.py
# Play 1kHz tone using piezo speaker
# Jan.11th 2023 (C) iot101 at zenn.dev

from machine import PWM, Pin
import time

# Frequency 
f = 1000

# Speaker is connected to GPIO26 pin
SPEAKER_PIN = 26

# Set duty 50%
DUTY = int(50 / 100 * 65535)

# Make speakder object
speaker = PWM(Pin(SPEAKER_PIN))

# Play sound
speaker.duty_u16(DUTY)
speaker.freq(f)
time.sleep(1)

# Stop sound, set duty 0
speaker.duty_u16(0)


サンプルコード2

周波数を400Hzから4kHzまで100Hzずつ変化させながら圧電スピーカに出力します。
説明用に行番号が入った画像ファイルを貼り付けていますが、コピペが必要な場合は、下のアコーディオンを展開して下さい。
alt説明用コード

コピペ用コード
pwm_400Hzto4kHz.py
# Play 400Hz to 4kHz tone using piezo speaker
# Jan.11th 2023 (C) iot101 at zenn.dev

from machine import PWM, Pin
import time


# Speaker is connected to GPIO26 pin
SPEAKER_PIN = 26

# Set duty 50%
DUTY = int(50 / 100 * 65535)

# Make speakder object
speaker = PWM(Pin(SPEAKER_PIN))

# Play sound
for f in range(400, 4100, 100):
    print("f =", str(f))
    speaker.duty_u16(DUTY)
    speaker.freq(f)
    time.sleep(0.1)

# Stop sound, set duty 0
speaker.duty_u16(0)

実行動画

サンプルコード3

先に求めた音階の周波数を元に、圧電スピーカを駆動するコードを作成します。
説明用に行番号が入った画像ファイルを貼り付けていますが、コピペが必要な場合は、下のアコーディオンを展開して下さい。
alt説明用コード

コピペ用コード
pwm_scale.py
# Play scale using piezo speaker
# Oct. 7th 2022 (C) iot101 at zenn.dev

from machine import PWM, Pin
import time

# Frequency of the scale
SCALE = [523, 587, 659, 698, 783, 880, 987, 1046]

# Speaker is connected to GPIO12 pin
SPEAKER_PIN = 26

# Set duty 50%
DUTY = int(50 / 100 * 65535)

# Make speakder object
speaker = PWM(Pin(SPEAKER_PIN), duty_u16=DUTY)

# Play scale
for f in SCALE:
    print(f)
    speaker.freq(f)
    time.sleep(1)

# Stop sound, set duty 0
speaker.duty_u16(0)

実行動画

演習問題

  1. サンプルコード3のプログラムを変更して、SCALEリストの音階からランダムな音階を出力し続けるプログラムを作成して下さい。
    (ファイル名:pwm-ex1.py)

  2. 次の楽譜を参照して「「カエルの歌」を演奏させるプログラムを作成して下さい。簡単にしたい場合は、2部音符(白玉)や8部音符(しっぽ付き)は全部4部音符として扱って下さい。
    (ファイル名:pwm-ex2.py)
    altカエルの歌楽譜

カエルの歌画像引用元:https://guitar-ukulele.net/index.php?カエルの歌楽譜

Discussion