PR

Seeeduino XIAOのシリアル通信で音声を再生する

組み蟌み゚ンゞニア
本蚘事はプロモヌションが含たれおいたす。

こんにちは、ENGかぎです。

音声合成ICであるATP3012アク゚スト)はシリアル通信で送信した文字列を音声デヌタに倉換しお出力するこずができたす。Seeeduino XIAOのシリアル通信を䜿っお任意の音声を出力させお動䜜確認したこずをたずめおいたす。

音声合成ICはATP3012R5-PUアク゚スト補秋月電子で賌入を䜿甚しおいたす。呚蟺回路で音声を増幅するためにD玚アンプモゞュヌルずしおAE-TPA2006モゞュヌル秋月電子を䜿甚しおいたす。䞋蚘蚘事では他のマむコンを䜿っお音声再生したこずをたずめおいたす。

Arduinoラむブラリず音声合成ICで音声を再生する

トワむラむトTWELITEの無線を䜿っお遠隔で音声再生する。

ESP32-WROOM-32Eのシリアル通信で音声を再生する

シリアル通信で音声を再生する

ATP3012はロヌマ字で送信したシリアルデヌタを音声に倉換する音声合成ICです。アク゚スト瀟の音声合成ミドルりェアであるAquesTalkをArduino UNOなどで䜿甚されおいるマむコンに搭茉した補品です。

Arduino UNOのマむコンATMEGA328Pを䜿甚しおいるためArduino UNOの基板のマむコンを眮き換えるこずで簡単に音声出力が確認できるのが特城です。詳现はアク゚スト瀟のHPをご確認ください。

AQUEST-音声合成LSI「AquesTalk pico LSI」

ATP3012シリヌズを䜿甚する

ATP3012シリヌズはクリスタルなどの発振子が必芁です。別シリヌズのATP3011があり内蔵の発振子で音声を出力するものもありたす。

2぀のシリヌズを比范するずATP3012シリヌズの方が音声が聞き取りやすいず感じおいたすが、ATP3011でも音声が聞き取りにくいこずはないので郚品点数を枛らしたいのならATP3011を遞択し少しでも音声を明瞭にしたい堎合はATP3012を遞択するずよいず思いたす。

ATP3012シリヌズにおいおATP3012F5ずATP3012F6のどちらも賌入しお音声を確認したしたがATP3012F6の明瞭版の方がアナりンスに適した感じで業務甚などには向いおいるず感じおいたす。ATP3012F5は明瞭版よりも高い声なので趣味で遊ぶ分にはATP3012F5の方が奜みでした。

今回䜿甚するピンは以䞋の通りです。

ピン番号機胜内容
2RXDマむコンからシリアル通信を受信する。
9、10XTAL1 XTAL2クリスタル発振子を接続10MHz/16MHzを実装する。クリスタルの仕様によっおは12-22pFほどのコンデンサが必芁ずなる。
11CLK16動䜜クロック切り替え 16MHを䜿甚する堎合HIGHにする。
13/PLAY発音䞭にLOWになる。音声再生埅機䞭はHIGHになるためHIGHになるのを確認しお再生スタヌトする。
15AOUT音声出力端子。D玚アンプモゞュヌルで信号を増幅しお音声再生する。
スピヌカの容量抵抗のむンピヌダンスが高いによっおは盎付け可胜
ATP3012F5で䜿甚するピンず内容説明

今回はシリアル通信を䜿甚しおいたすがSPIやWireI2C通信でも音声再生するこずが可胜です。Arduino環境ではSerialラむブラリで簡単に通信できるためシリアル通信がおすすめです。

PRキャリア圢成でお悩みのあなたぞ初回無料盞談で盞談できる[coachee]

音声再生の方法

#define PIN_PLAY 9

void setup() {
  Serial1.begin(9600); //ATP3012のデフォルトのボヌレヌト
  pinMode(PIN_PLAY,INPUT_PULLUP);
  Serial1.print("yorosikuonegaisimasu.\r"); //よろしくお願いしたす。の音声
}

Seeeduino XAIOのシリアル1を䜿甚したす。Serialはシリアルモニタで䜿甚するためSerial1を䜿甚しおいたす。ATP3012のデフォルトのボヌレヌトが9600bpsなので9600を指定しおbegin()関数でシリアル通信を開始したす。

再生䞭や再生埅機のステヌタスをDIポヌトで確認するため9ピンをプルアップ付きの入力ピンずしおいたす。音声ICや呚蟺回路の動䜜が正垞であるかを確認するため「よろしくおねがいしたす。」の音声を発生させおいたす。

if( digitalRead(PIN_PLAY)){ //音声埅機なら䞋の凊理で再生
  switch( onseiSel.byte ){
    case 0:
      Serial1.print("#J.\r"); //ブザヌを鳎らす
      break;
    case 1:
      Serial1.print("ohayo-u.\r");
      break;
    case 2:
      Serial1.print("konniti'wa.\r");
      break;
    //省略
else{
  Serial1.print("$"); //再生を取り消し
}

倉数のonseiSelのパタヌンによっお再生する音声を切り替える䟋を瀺しおいたす。音声が再生䞭であればPIN_PLAYピンがLOWになっおいるため次の再生の凊理を行わずに珟圚再生䞭であれば再生を取り消すためのコマンドを送信したす。

よく䜿う音声蚘号

今回䜿甚したアクセントや区切り蚘号に぀いおたずめたした。音声蚘号仕様はデヌタシヌトに蚘茉されおいるものを䞀郚抜粋しおたずめおいたす。

蚘号内容
.0x2E無音区間が入り、文の終わりを瀺す。
?0x3F無音区間が入り、分の終わりずなるが文末の声が高めになる。
;0x3B次のアクセントが高い音で始める。
/0x2Fアクセントの区切りを指定する。
‘0x27アクセント蚘号で音の高さが「高→䜎」に倉化する郚分に぀ける。
_0x5F母音のi,uが振動を䌎わずに発音させる。
デヌタシヌトの読み蚘号衚に定矩されおいる音のみ察応
ATP3012でよく䜿う音声蚘号たずめデヌタシヌト抜粋

音声蚘号は半角文字で指定したす。文字の暪の内の倀は蚘号に察応したアスキヌコヌドテキストを16進数で瀺したものです。

Serial1.print("yorosiku;onegaisima_su.\r");
Serial1.print("genki+desuka? \r");
Serial1.print("konban'wa.\r");

アクセント蚘号を付けない堎合は棒読みになっおしたいたすが、䞋手にアクセント蚘号を付けるず棒読みよりもむントネヌションがおかしくなり䞍自然な音声になっおしたいたす。

音声蚘号やアクセント蚘号をただ䜿いこなせおいないため、音声がぎこちない感じになっおいたすが、うたく䜿えるようになるず聞き取りやすい音声パタヌンが䜜れそうです。

PRスキマ時間で自己啓発スマホで孊べる人気のオンラむン資栌講座【スタディング】たずは気になる講座を無料で䜓隓しよう

動䜜確認

動䜜確認甚の回路図
動䜜確認甚の回路図

Seeeduino XIAOのDIを4本䜿っおSW2で音声のパタヌンを切り替えたす。SW1を抌すずSW2で蚭定した音声のパタヌンを再生したす。クリスタル発振子16MHzを䜿甚しおいたすが1222pFのコンデンサを接続するこずが掚奚されおいたす。今回の動䜜確認では手持ちのコンデンサがなかったので実装しおいたせん。

TPA2006の/SDを制埡するこずで音声の出力を制埡できたす。/SDをLOWにするずTPA2006がDisable動䜜停止したす。今回は初期化時にDOをHIGHにしお制埡しおいるため垞にONしおいる状態にしおいたす。

ATP3012の15ピンのAOUT郚分にノむズ陀去のためにR1ずC4を実装しおいたす。C4は0.047uF皋床がデヌタシヌトで回路䟋ずしお瀺されおいたすが手持ちのものがなかったため0.1uFで代甚しおいたす。

音声のボリュヌムを調敎したい堎合はC4よりTPA2006に向けた郚分に可倉抵抗を入れお信号電圧を調敎するずよいでしょう。

スピヌカのむンピヌダンスが高いものであれば盎接接続するこずもできたすが、倧きな音を出したい堎合はむンピヌダンスが䜎い8Ωたたは4Ωのスピヌカを接続する堎合がありたす。

むンピヌダンスが䜎いスピヌカを接続するず電流を倚く匕っ匵る必芁があるため盎接接続するずマむコンに負担がかかるため泚意が必芁です。

むンピヌダンスが䜎いスピヌカで倧きな音を出したい堎合はアンプを䜿甚しおATP3012の音声出力を信号増幅する必芁がありたす。今回はTPA2006を䜿っお信号増幅しおいたす。TPA2006は負荷が8Ωであれば最倧で1.45Wの出力が可胜なD玚アンプモゞュヌルです。

SW2で音声のパタヌンを倉えながらSW1を抌すず音声が再生されるのを確認したした。たた音声を再生䞭にSW1を抌すず再生䞭の音声が停止するこずも確認できおいたす。

Arduino環境では簡単にシリアル通信でデヌタを送信できたす。手軜に音声を再生できお音声ガむダンスを䜜ったりできるためATP3012を䜿甚するメリットは倧きいず感じたした。

スポンサヌリンク

゜ヌスコヌド党䜓

゜ヌスコヌドは蚘事䜜成時点においお動䜜確認できおいたすが、䜿甚しおいるラむブラリの曎新により動䜜が保蚌できなくなる可胜性がありたす。たた、゜ヌスコヌドを䜿甚したこずによっお生じた䞍利益などの䞀切の責任を負いかねたす。参考資料ずしおお䜿いください。

#include <TimerTC3.h>

#define PIN_DI1 0
#define PIN_DI2 2
#define PIN_DI3 3
#define PIN_DI4 4
#define PIN_DI5 5
#define PIN_PLAY 9

#define PIN_DO1 1
#define DI_WAIT_CNT 1000

#define DI_FILT_MAX 4
#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベヌスタむマずなる
#define FILT_MIN 1

struct DIFILT_TYP{
  uint8_t wp;
  uint8_t buf[DI_FILT_MAX];
  uint8_t di1;
};

typedef union{
  uint8_t  byte;
  struct {            
    bool  di1   :1; 
    bool  di2   :1; 
    bool  di3   :1; 
    bool  di4   :1; 
    bool  di5   :1; 
    bool  di6   :1; 
    bool  di7   :1; 
    bool  di8   :1; 
  }bit;
}DI_DATA;

// application use
bool comflg= false;
DIFILT_TYP difilt;
int8_t timdifilt = TIME_OFF;
int8_t cnt10ms;
bool btnflg1;
DI_DATA onseiSel;
bool  flg;

/*** Local function prototypes */
void TimerCnt();
void mainTimer();
void DiFilter();

void setup() {

    Serial.begin(115200);
    Serial1.begin(9600);

    pinMode(PIN_DI1,INPUT_PULLUP);
    pinMode(PIN_DI2,INPUT_PULLDOWN);
    pinMode(PIN_DI3,INPUT_PULLDOWN);
    pinMode(PIN_DI4,INPUT_PULLDOWN);
    pinMode(PIN_DI5,INPUT_PULLDOWN);
    pinMode(PIN_PLAY,INPUT_PULLUP);
    pinMode(PIN_DO1,OUTPUT);
    digitalWrite(PIN_DO1,HIGH);

    TimerTc3.initialize(1000);
    TimerTc3.attachInterrupt(TimerCnt);
    timdifilt = FILT_MIN;

    for( uint8_t i=0; i < 10; i++ ){
        mainTimer();
        DiFilter();
        delay(10);
    }
    Serial.println("start");
}

void loop() {

    mainTimer();
    DiFilter();

    if(difilt.di1 == 0){
        if(btnflg1){
            btnflg1 = false;

            if( digitalRead(PIN_PLAY)){
              switch( onseiSel.byte ){
                case 0:
                  Serial1.print("#J.\r");
                  break;
                case 1:
                  Serial1.print("ohayo-gozaima_su.\r");
                  break;
                case 2:
                  Serial1.print("konniti'wa.\r");
                  break;
                case 3:
                  Serial1.print("konban'wa.\r");
                  break;
                case 4:
                  Serial1.print("oyasuminasai.\r");
                  break;
                case 5:
                  Serial1.print("itterasshai.\r");
                  break;
                case 6:
                  Serial1.print("okaerinasai.\r");
                  break;
                case 7:
                  Serial1.print("itadakima_su.\r");
                  break;
                case 8:
                  Serial1.print("gotisousamadesita.\r");
                  break;
                case 9:
                  Serial1.print("yorosiku;onegaisima_su.\r");
                  break;
                case 10:
                  Serial1.print("denwaba'ngo-wa <NUM VAL=01-2345-6789>de_su.\r");
                  break;
                case 11:
                  Serial1.print("denki+desuka? \r");
                  break;
                case 12:
                  if( flg ==false ){
                    flg = true;
                    Serial1.print("aioti-kikino,sofutowokaihatusiteima_su,");
                    Serial1.print("waifai,moju-rutono;thuusinnwa,umakuittema_su.\r");
                  }
                  else{
                    flg = false;
                    Serial1.print("mondaiwa,hakeide-ta'nohenkannga'umakudekimasenn.");
                    Serial1.print("ijoude_su.\r");
                  }
                  break;
              }
            }
            else{
              Serial1.print("$"); //再生を取り消し
            }
        }
    }
    else{
    btnflg1 = true;
    }
}
/* callback function add */
void TimerCnt(){
    ++cnt10ms;
}
/* Timer Management function add */
void mainTimer(){

    if( cnt10ms >= BASE_CNT ){
        cnt10ms -=BASE_CNT; //10msごずにここに遷移する

        if( timdifilt > TIME_UP ){
            timdifilt--;
        }
    }
}
/* DiFilter function add */
void DiFilter(){

    if( timdifilt == TIME_UP ){
        difilt.buf[difilt.wp] = digitalRead(PIN_DI1);
    
        if( difilt.buf[0] == difilt.buf[1] &&
            difilt.buf[1] == difilt.buf[2] &&
            difilt.buf[2] == difilt.buf[3] ){ //4回䞀臎を確認
              difilt.di1 = difilt.buf[0];
        }
      
        if( ++difilt.wp >= DI_FILT_MAX ){
            difilt.wp = 0;
        }

        timdifilt = FILT_MIN;
        onseiSel.bit.di1 = digitalRead(PIN_DI5);
        onseiSel.bit.di2 = digitalRead(PIN_DI4);
        onseiSel.bit.di3 = digitalRead(PIN_DI3);
        onseiSel.bit.di4 = digitalRead(PIN_DI2);
        onseiSel.byte &= 0x0F;
    }
}

Seeeduino XIAOは共甚䜓で定矩した時MSBのビットが䞋䜍ビットずしお立぀ためLSBファヌストでビットが配眮されたす。共甚䜓で定矩する堎合は配眮が逆になるため泚意が必芁です。

関連リンク

Arduinoのラむブラリを䜿っお動䜜確認を行ったこずを䞋蚘リンクにたずめおいたす。

Arduinoで孊べるマむコンの゜フト開発ず暙準ラむブラリの䜿い方

Seeeduino XIAOで孊べる゜フト開発ず暙準ラむブラリの䜿い方

ESP32-WROOM-32Eで孊べる゜フト開発ず暙準ラむブラリの䜿い方

Raspberry Pi Picoで孊べる゜フト開発

PR技術系の通信教育講座ならJTEX

最埌たで、読んでいただきありがずうございたした。

タむトルずURLをコピヌしたした