もよいめも

不定期更新ものづくりブログ

【Arduino】Raspberry Pi Picoと圧電ブザーだけで好き放題しゃべらせる(スピーカーでも)

どうも、そういえば先日愛車が事故で廃車になったんですけど、
日々マイコンで色々作っていると、圧電ブザーを使う機会が良くありますよね(???)

愛車のヘッドライトつけっぱなしでドア開けた時になる警告音も圧電ブザーだったのかなぁ...

まあそんなことはおいといて、
ブザーは大変高インピーダンスであるため、アンプ不要で直接マイコンから駆動できる上に消費電力も極めて少ない、非常に優秀な部品ではありますが、
出せる音といったら単音だけなイメージ

実際のところ、ブザーで音楽や声を流すことはできるのか?
今回は、そんなことを試してみましたのでご紹介いたします。

結論

マイコンとブザーだけの場合
ギリ聞こえる

やり方

まず、音声ファイルを用意します

今回は以下サイトで適当に生成した音声を使用させていただきます。
www.voicevox.su-shiki.com

そしたら以下PythonコードでWAVファイルを解析して、生データを配列として記録したヘッダーファイルに変換

import numpy as np
import wave
import os
import tkinter as tk
from tkinter import filedialog

iDir = os.path.abspath(os.path.dirname(__file__))
os.chdir(iDir)

filetype = [ ("WAVファイル","*.wav"), ("すべて","*")]
file_path = tk.filedialog.askopenfilename(filetypes = filetype, initialdir = iDir)

w = wave.open(file_path, 'rb')
data = w.readframes(w.getnframes())
w.close()

s = np.frombuffer(data, dtype="int16") 

f = open(os.path.splitext(os.path.basename(file_path))[0]+'.hpp', 'w')
counter = 0
print(int(w.getframerate()))
f.write('#pragma onceu\n')

f.write('\n')
f.write('const uint16_t len_'+os.path.splitext(os.path.basename(file_path))[0]+' = '+str(len(s))+';\n')
f.write('PROGMEM const int16_t sound_'+os.path.splitext(os.path.basename(file_path))[0]+'[] = {\n')
for rawSound in s:
    f.write(str(hex(int(rawSound)))+',')
    counter+=1
    if counter == 100:
        counter = 0
        f.write('\n')
f.write('};')
f.close()
生成されたmazimanzi.hpp

ターミナルに音声ファイルのサンプリングレートも吐き出されるので、別途記録しておきます。

サンプリングレート

そしたらArduino IDDEで新たなスケッチを作成して保存、同じディレクトリに生成されたヘッダファイルをぶち込みます。

今回はPROGMEM呪文によってRAMではなくフラッシュに音声データを保存しているので、他のプログラムの規模によりますが2Mbyteくらいの音声を再生できます。
今回の音声データだと約9秒で5%なので、max180秒つまりは3分くらいは保存しておけるかもですね。

コンパイル後のファイル容量

そして以下が肝心のプログラム。
今回はPWMaudio機能を利用して何とか鳴らないか試してみました。
PWM Audio Library — Arduino-Pico 3.6.0 documentation
ちなみに、PWMの周波数は48KHz固定の様子、人間の可聴域は20KHzくらいまでなので一応外れています。

#include <PWMAudio.h>
#include "mazimanzi.hpp"

PWMAudio pwm(0);

void setup() {
  pwm.begin(24000);//サンプリングレートを入力
}

void loop() {
  for (int i = 0; i < len_mazimanzi; i++) {
    pwm.write(sound_mazimanzi[i], true);//音声をバッファに追加(trueで空きがない場合待機)
  }
  delay(1000);
}

結果はこちら↓
youtu.be

う~ん、ギリ聞こえるかなって感じ。
マイコンから直だとあんまり実用的とは言えなそうです。

(マイコンから直接駆動させる場合は抵抗を挟んだ方がいいです)
www.murata.com



ブザードライバーを挟めばなんとかなるかなとか思いましたが、
さすがに入力が8KHzまでなので厳しそう
NJU72501使用チャージポンプ内蔵圧電スイッチングドライバモジュール: 半導体 秋月電子通商-電子部品・ネット通販

Hブリッジ組んで高い電圧をかけるか...?
そこまでするならLPFをかませてオーディオ周波数に落としてスピーカーアンプ通して普通のスピーカーつないだ方がよっぽど幸せになれる気がします。

おまけ I2Sアンプでスピーカを使って鳴らす

少々値段が張りますが、市販のI2Sアンプに適当に配線すれば超簡単にそれなりの音量・音質でしゃべらせられます。
MAX98357搭載 I2Sアンプモジュールwww.switch-science.com

#include <I2S.h>
#include "mazimanzi.hpp"

#define pBCLK 12
#define pWS (pBCLK + 1)
#define pDOUT 11

I2S i2s(OUTPUT);

void setup() {
  i2s.setBCLK(pBCLK);
  i2s.setDATA(pDOUT);
  i2s.setBitsPerSample(16);
  i2s.begin(24000)
}

void loop() {
  for (int i = 0; i < len_mazimanzi; i++) {
    sample = sound_mazimanzi[i]; 
    i2s.write(sample);//モノラルアンプに入力する場合も2回writeする必要あり
    i2s.write(sample);
  }
  req_sound = NON;
}


Xにあげた動画っていい感じに埋め込めるんすね

そういや記事を書きながら、ブザーでずんだもん(変換で図んだもんになるのがうざい)がささやく「マジ卍」を聞き続けていたら、なんだか「マジで熱い」に聞こえてくるようになりました。

以上終わり!

(今月も雑記事でなんとかノルマ達成...)
moyoi-repo.blogspot.com