(27) 【連載1-6】マイコンで電子工作入門:合成音声で時報

投稿者: | 2018年5月9日

この記事は最終更新から 727日 が経過しています。

1. やってみたいこと

(24) 【1-4】マイコンで電子工作入門:時報を作る ではラズパイと音源ICを使って時報を作ってみた。

壁に掛けたラズパイからピッタリ 1時間ごとに「ピコピコ♪ ピーピ♪」と音が鳴り気に入っていたのだが、同居している家族からはとっても不評だ。

電子音が耳障りだ…

とのこと。

仕方がないので、再生時間を曲の先頭 3秒だけにして運用していたが、今度は自分が面白くない。

こんなことで子供を巻き込んで夫婦喧嘩をしても仕方がないので、対策を検討してみた結果…

電子音がイヤなら音声で時報を作ろう!

と考えた。

家族会議を経ていないので、また今度も 「不快だ」 と言われる可能性もあるが、
「○時□分です。」
としゃべらせるだけなら電子音の許容限界であった 3秒以内に収まるし、文句はないと信じて着手する。

2. 音声時報の実現方法

Linuxマシンであるラズパイを使うので方法はいくらでもある。
サウンドファイルを作り、スピーカー出力端子に外部スピーカーを繋げて再生するのが簡単だろう。

でも…
それでは電子工作っぽくない。

そこで…

ブレッドボード上の音声合成 ICで声を作り、
ブレッドボード上の圧電スピーカーで再生する

という実現方法に決めた。

3. パーツを調達

今回は秋月さんでこちらの音声合成 LSIを買ってきた。

この音声合成LSI
AquesTalk pico LSI ATP3011F
は、自分が秋月さんで購入する物の中ではかなり高額な 1個 850円。

ボーカロイドのように声別に製品がわかれており、以下のラインナップがある。
・かわいい女性
・ゆっくりな女性
・男性
・ロボット声

今回は「かわいい女性」にした。

秋月さんでは ATP3011F と ATP3012F が売られていた。
最初は「ATP3012F 女性の声明瞭版」にしようと考えていたが、3012は外部クロックの供給が必要なため、実装時にひと手間増える。

今回は実装で少し楽をするために、内蔵クロックで動作する ATP3011F の方を選択した。


https://www.a-quest.com/products/aquestalkpicolsi.html

4. 回路の設計

(1) ラズパイと ICの結線

この音声合成 LSIには足が 28本もあるので目がくらんでしまうが、実際に繋ぐ必要のある足数は少ない。
ATP3011Fのデータシート を見ながら考えてみる。

1) バスプロトコルの選択

ATP3011Fはコントローラ接続用に複数のバスプロトコルをサポートしている。
今回は信号線の本数が 2本と少なくて済む I2Cを選択する。

他の通信プロトコルに変更するなど運用中に変更する可能性は 0なので、以下のように GNDと VCCに繋いで通信プロトコルを I2C固定としてしまう。
・SMOD0: 0 (LOW) - GND
・SMOD1: 1 (HIGH) - VCC
※デフォルトで IC内部でプルアップされているので SMOD1はオープンでよい。

2) I2Cの接続

ラズパイと ICの I2C信号線を繋ぐ。
これはラズパイ側でも繋ぐ場所が決められている。
・SDA - GPIO2 (SDA)
・SCL - GPIO3 (SCL)
※データシートに従い、この 2本は 5kΩでプルアップする。

3) 動作モードの選択

ラズパイから I2C経由でコマンドを送って音を鳴らす「コマンド入力モード」を選択する。
これも運用中に変更するつもりはないので、このモード固定とする。
・PMOD0: 1 (HIGH)
・PMOD1: 1 (HIGH)
※デフォルトで IC内部でプルアップされているので、外部 VCCへの接続は不要だ。

4) スリープ制御

時報は 1時間に 1回しか動かさないので、不要な時はこの ICをスリープ状態(=省電力モード)にしておきたい。
このため ICの SLEEPピンをラズパイの GPIO17 と繋ぎ、マイコンからソフトで制御できるようにしておく。
・/SLEEP - GPIO17
※ACTIVE LOWなのでスリープさせたい場合は GPIO17経由で LOW信号を入れる。

つまり…
・時報を再生中は HIGH
・時報の再生が終了したら LOW
のように SLEEP端子の入力信号レベルを制御する。

5) スピーカーと接続

AOUT端子は回路例に従ってブレッドボード上で圧電スピーカーと繋ぐ。

6) 結線のまとめ

下図の 赤枠内 のピン(全10本)だけブレッドボード上で繋げばよい。

注意

上の回路図は論理的に図示してあり、離れた場所にあるピンを隣接しているかのように書いてある。
実際のピン配置は下図の通りなので注意する。

(2) ブレッドボード上に展開

上記の 「(1)-6) 結線のまとめ」 に従ってブレッドボード上の配線を書いてみた。

あんまり整理とか考えずに書いてみたので汚いか…

で…
実際の実装はこちら。

5. マイコン制御ソフトの設計

習うより慣れろ!

の個人的な方針に従い、次項で実際に動かしながらプログラムを仕上げていく。

よって…
プログラム設計フェーズは設けない。

6. やってみる!

(1) ラズパイで I2Cを有効にする。

sudo raspi-config

[Interface]-[I2C] enabledに設定する。

ATP3011Fのデータシートの「9.2 I2C通信」では、I2Cスレーブアドレス初期値は 0x2Eと書かれている。
ラズパイ側でどう認識されているか確認してみると?

pi@rp3:~ $ sudo i2cdetect -l
i2c-1   i2c             bcm2835 I2C adapter                     I2C adapter
pi@rp3:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 2e --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@rp3:~ $

確かに 0x2Eにデバイスが検出された。
このアドレス値は ATP3011F上の EEPROMに保存されている値であり、自由に書き換えることもできる。
今回は I2Cバス上にアドレスが競合するデバイスもいないので、このまま使用する。

(2) 事前準備

pythonを起動する。

python

ATP3011Fに対して単発コマンドを送る準備をする。

import RPi.GPIO as GPIO
import smbus

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)  # GPIO17を出力ポートに設定(SLEEP制御用)
bus = smbus.SMBus(1)      # I2C初期設定

def ATP3011_send_cmd( cmd ):  # コマンド送信関数
    cmd0 = ord(cmd[0])
    cmd1 = []
    for c in cmd[1:]:
        cmd1.append(ord(c))
    GPIO.output(17, GPIO.HIGH)                   # SLEEPモードを解除
    bus.write_i2c_block_data(0x2e, cmd0, cmd1)   # コマンド送信実行
    GPIO.output(17, GPIO.LOW)                    # SLEEPモードに移行

(2) ラズパイから単発コマンドを送って声を出してみる。

1) チャイムを鳴らす。

「ピーン」とベルの音がする。

cmd = "#K\r"
ATP3011_send_cmd(cmd)

こっちはかすかに「ピンポン」と聞こえるが、その 3倍ぐらいの音量で「ジージー」とノイズが聞こえる…

cmd = "#J\r"
ATP3011_send_cmd(cmd)

2) 初めてしゃべらせる。

「あいうえお」

cmd = "aiueo\r"
ATP3011_send_cmd(cmd)

「いち」「にい」「さん」「よん」

cmd = "ichi\r"
ATP3011_send_cmd(cmd)

cmd = "nii\r"
ATP3011_send_cmd(cmd)

cmd = "sann\r"
ATP3011_send_cmd(cmd)

cmd = "yonn\r"
ATP3011_send_cmd(cmd)

3) 時刻をしゃべらせる。

「1時です」

cmd = "ichijidesu\r"
ATP3011_send_cmd(cmd)

「1時 です」 少し間を空ける。

cmd = "ichiji desu\r"
ATP3011_send_cmd(cmd)

「1時,です」 ほんの少し間を空ける。

cmd = "ichiji,desu\r"
ATP3011_send_cmd(cmd)

時報には「ほんの少し」ぐらいがよいかも

指定時刻用のコマンドを作ってくれる関数 get_cmd_Jiho() を作る。

ary_h = ['zero','ichi','ni','san','yo','go','roku','siti','hati','ku','juu','juuichi','juuni','juusan','juuyo','juugo','juuroku','juusiti','juuhati','juuku','nijuu','nijuuichi','nijuuni','nijuusan']
def get_cmd_Jiho( h ) :
    return ary_h[h] + 'ji,desu\r'

実際に使ってみる。

import time
for i in range(0,24):
    cmd = get_cmd_Jiho(i)
    ATP3011_send_cmd(cmd)
    time.sleep(3)

0時から 23時まで声に出して読み上げてくれた。

(3) 一つのプログラムファイルにまとめる。

現在の時刻値を取得し「○時です。」としゃべってくれる 1個のプログラムファイル test27.py にまとめる。
時報を読み上げる直前に、チャイムの音 x 3回を鳴らすようにした。

import RPi.GPIO as GPIO
import smbus
from datetime import datetime
import time

ary_h = ['zero','ichi','ni','san','yo','go','roku','siti','hati','ku','juu','juuichi','juuni','juusan','juuyo','juugo','juuroku','juusiti','juuhati','juuku','nijuu','nijuuichi','nijuuni','nijuusan']

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)      # GPIO17を出力ポートに設定(SLEEP制御用)
bus = smbus.SMBus(1)          # I2C初期設定

def get_cmd_Jiho( h ) :       # 時報用の音声コマンドを作成
    return ary_h[h] + 'ji,desu\r'

def ATP3011_send_cmd( cmd ):  # ATP3011Fへのコマンド送信
    cmd0 = ord(cmd[0])
    cmd1 = []
    for c in cmd[1:]:
        cmd1.append(ord(c))
    GPIO.output(17, GPIO.HIGH)                   # SLEEPモードを解除
    bus.write_i2c_block_data(0x2e, cmd0, cmd1)   # コマンド送信実行
    GPIO.output(17, GPIO.LOW)                    # SLEEPモードに移行

h   = datetime.now().hour           # 現在の時刻を取得
cmd = get_cmd_Jiho(h)               # ATP3011Fに送信するコマンドを作成

for i in range(0,3):                # チャイムを 3回鳴らす。
    ATP3011_send_cmd('#K\r')
    time.sleep(1)

ATP3011_send_cmd(cmd)

GPIO.cleanup()

これを Terminalから実行してみる。

python test27.py

現在 15時 28分、
「ピーン♪」
「ピーン♪」
「ピーン♪」
「15時 です。」

としゃべってくれた。

(4) 周期起動させて時報にする。

これは (24) 【1-4】マイコンで電子工作入門:時報を作る とまったく同じことをすればよい。

crontabコマンドを実行する。

crontab -e

毎時 0分に実行するように設定する。

0 * * * * python /home/pi/MyWork/test27.py

音声で読み上げる時報ができた!

7. 応用

いつか時間と気持ちの余裕が出来たらやってみようと思うこと。

(1) 感情表現を加える。

今回使用した ATP3011F では、しゃべらせる文に抑揚をつけることが出来る。
時報でしゃべる短文ではあんまり意味がないかもしれないが、いつかやってみよう。

https://www.a-quest.com/archive/manual/atp3011_datasheet.pdf

(2) 音量アップのためにアンプを繋げる。

音声読み上げの時報が出来るには出来たのだが、

ノイズ交じりの蚊の羽音のような声しか聞こえない…

そもそも今回の制作は、同居する家族から
「電子音の時報がうるさい」
と怒られて始めたものであり、静かな分にはよいのかもしれない。

だが、スピーカーから 1mの距離でも耳をこらさないと聞こえないレベルなので、実用的とは言えない。

もう候補は決めてある。コレ(↓)をシングルエンド入力で使用する。

アンプICを購入すれば、その周辺にはデータシートの実装例に従ってコンデンサやら抵抗やらを繋がなければならない。
こちらのキットは付属の基板上にそれらを実装済みなので、AOUTと別電源を繋ぐだけで使えてしまう。

いつかアンプを繋げてスピーカーの音量を大きくし、くっきりボイスで聴きたいなぁ…

(3) ノイズ除去フィルターを付ける。

スペアナで周波数成分を見てみたいが、そんな物は持っていない…
スペアナ: 周波数解析器 (Spectrum Analyzer) ヤフオク

おそらく「ビービー」鳴っている高周波数帯域をごっそり切ってよいはず。
すなわち、LPF(Low Pass Filter)で音声データの存在する帯域のみを通過させてスピーカーに入力する。

7. 所感

I2Cでラズパイと繋ぐことができる面白いデバイスがあれば、どんどん繋いでみたい。
根回りの通信制御は SMBusがラップしてくれているので、実装時は上位のプロトコルだけを意識すればよい。

今回初めて足が 28本(片側14本 x 2列)もある ICをブレッドボード上に載せた。
占有面積で見ればわずかな大きさなのだが、実際にはブレッドボード上で横方向に 14本/30本も占有してしまう。
こんな ICを 2個、 3個と繋ぐ回路を作る場合は、ブレッドボードが複数枚必要になるなぁ…

参考情報

ありがとうございます。 m(_ _)m
https://abiko-toy-hospital.jimdo.com/%E8%B3%87%E6%96%99%E9%9B%86-1/vcc-vee-vdd-vss%E3%81%A8%E3%81%AF/
http://www.picfun.com/c15.html
http://www.geocities.jp/zattouka/GarageHouse/micon/TalkIC/ATP3011.htm
http://www.raspberry-projects.com/pi/programming-in-python/i2c-programming-in-python/using-the-i2c-interface-2
http://blog-yama.a-quest.com/?eid=970162
http://www.cqpub.co.jp/dwm/contents/0053/dwm005300950.pdf

連載

Raspberry Piの初期設定から簡単な動作実験まで、以下はマイコン制御入門の連載です。
(21) 【連載1-1】マイコンで電子工作入門:選定
(22) 【連載1-2】マイコンで電子工作入門:ラズパイ初期設定
(23) 【連載1-3】マイコンで電子工作入門:LED点滅
(24) 【連載1-4】マイコンで電子工作入門:時報を作る
(26) 【連載1-5】マイコンで電子工作入門:サーボモーターを廻す
(27) 【連載1-6】マイコンで電子工作入門:合成音声で時報


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)