Help us understand the problem. What is going on with this article?

ESP8266 (ESP-WROOM-02) 映画「ちはやふる」公開記念!音声合成LSI で百人一首の練習マシーンを作る

More than 3 years have passed since last update.

概要

音声合成IC で百人一首の練習マシーンを作る

はじめに

5月27日は百人一首の日
藤原定家によってこの日、百人一首が完成されたと言われています。

「ちはやふる」主演の広瀬すず、可愛いですね!!「海街diary」もよかったす!!

今回は「ちはやふる」公開記念ということで、ESP8266(以下 WROOM02) で、音声合成LSI を使って百人一首の練習マシーンを作ろう!!という企画です。

i12-82-08c.png

完成イメージ

  • 一人でもくもくと、百人一首の練習をするためのマシーン
    • WROOM02 が、ランダムに読み上げる
    • 練習用札を「バシッ」と払うと、読み上げが中断する
    • 元の場所に戻すと、次の札の読み上げ再開

部品調達

部品は全て秋月電子で調達しました。

回路

音声合成LSI ATP3011 ですが、SPI でも通信可能ですが、今回は I2C で。
2線なので、ほんとお手軽に配線できます。

スクリーンショット_2016-05-24_12_26_32.png
※ 配線中、最初だけは「デモモード」にしておくと便利です。

スクリーンショット_2016-05-24_12_26_46.png

音声合成LSI ATP3011 はピン数が多いですが、上記 4個 のモード設定と、I2C、スピーカ出力だけですね。簡単です。

スクリーンショット 2016-05-24 12.48.31.png

  • そういえば最近、upverter で ちゃんとした回路図、書いてないなぁ。。。

実験スケッチ

シリアルモニタからローマ字を入力すると、内容をしゃべります。

<例>
konnichiwa

#include <Wire.h>

// ATP3011 I2C Address
const uint8_t ADDR = 0x2E;

void setup() {
  Serial.begin(115200);
  Serial.println("");
//Wire.begin(4, 5); // (SDA,SCL) Default
  Wire.begin(); // (SDA,SCL)

  delay(200);
}

void check_serial() {
  if (0 < Serial.available()) {
    delay(10);
    String buff;
    while (0 < Serial.available()) {
      char ch = Serial.read();
      if (ch == '\n') {
        speak(buff);
        Serial.println(buff);
      } else if (0x20 <= ch && ch <= 0x7f) {
        // ASCII文字コード
        buff = buff + ch;
      } else {
        // Do nothing
      }
    }
  }
}

void loop() {
  check_serial();
  if (isBusy()) {
    Serial.println("speaking...");
    delay(500);
  }
}


bool isBusy() {
  Wire.requestFrom(ADDR, (uint8_t)1);
  while (true) {
    while (Wire.available()) {
      char b = Wire.read();
      // コマンド実行中は Busy 状態になり、この間に他のコマンドを受信すると、コマンド応答として '*' が返ります。
      // なお、発話中は Busy 状態となります。
      return b == '*';
    }
    delay(100);
  }
}

void speak_stop() {
  speak("$");
}

void speak(String buff) {

  uint8_t *data = (uint8_t *)buff.c_str();
  write_data(ADDR, data, buff.length());

  // 1つのコマンドは 1byte~127byte の ASCII 文字列の最後にデリミタとして CR(0x0D)を付与します。
  // AT30110F4 は、この CR の受信後にコマンドを実行します。なお、コマンドの大文字、小文字は区別されます。

  uint8_t cmd[] = {0x0D};
  write_data(ADDR, cmd, 1);

  delay(1);
}

/////////

void write_data(uint8_t addr, uint8_t *data, size_t len) {
  // wireライブラリは最大32バイトなので分割して送信する
  const size_t max = 32;
  size_t n = len / max;
  size_t m = len % max;
  size_t i = 0;
  for (; i < n; i++) {
    write(addr, &data[i*max], max);
  }
  write(addr, &data[i*max], m);
  delay(1);
}

void write(uint8_t addr, uint8_t *data, size_t len) {
  Wire.beginTransmission(addr);
  Wire.write(data, len);
  Wire.endTransmission();
  delayMicroseconds(27); // 26.3us
}

工作

取り札一枚と磁石スイッチ、両面テープを用意します。
IMG_2631.JPG

取り札と磁石スイッチを、両面テープで貼り付けます。
IMG_2630.JPG

磁石スイッチが近接していると、OFFになります。
IMG_2633.JPG

取り札を払うと、磁石スイッチがONになります。
IMG_2632.JPG

つまり、

  • 取り札がセットされている間は句が読まれて、「バシっ」と札を払うと、発話が停止されます。
  • 再度取り札をセットすると、次の句の頭から、再開されます。

これで、こころおきなく、コソ練ができますね。

スケッチ

GitHub はこちら
https://github.com/exabugs/chihayafuru

#include <Wire.h>
extern "C" {
#include "user_interface.h"
}

// ATP3011 I2C Address
const uint8_t ADDR = 0x2E;

const int SENSOR_PIN = 13;

// 百人一首 読み
String data[] = {
  "a'kinotano-,karihono/iono,to'mao/ara'mi-  wa'gakoromodewa-,tu'yuni/nuretutu-", // 1
  "ha'rusugite- na'tukinikerashi-,shirotaeno-  koromoho'sucho-,a'mano/kaguyama-", // 2
  "ashibikino- yama'dorinoono,shidariono-  naganagashiyoo-,hitorikamonenn-", // 3
  "tagonourani- uchiidetemireba,shirotaeno-  fu'jinotakaneni-,yuki'wa/furitutu-", // 4
  "okuyamani- mo'miji/fumiwake,nakushikano-  ko'e/kikutokizo,a'kiwakanashiki-", // 5
  "kasasagino- wataseruhashini,okushimo'no-  si'rokio/mi'reba,yo'zo/fuke'ni/ke'ru-", // 6
  "amanohara- furisakemireba,ka'suganaru-  mikasanoyamani,i'desi/tuki'kamo-", // 7
  "wa'gaiowa- miyakonotatumi,shika'zo/su'mu-  yo'oujiyamato,hitowayu-nari-", // 8
  "hana'noirowa- uturinikerina,itadurani-  wa'gami/yo'ni/fu'ru,nagamesesimani-", // 9
  "koreyakono- yuku'mo/kae'rumo,waka'retewa-  siru'mo/sira'numo,o-sakanoseki-", // 10
  "watanohara- yasosimakakete,kogiidenuto-  hito'niwatugeyo,a'mano/turibune-", // 11
  "amatukaze- ku'mono/kayoiji,fukitojiyo-  oto'meno/su'gata,si'basi/todomen-", // 12
  "tukubaneno- mi'neyori/otu'ru,minano'gawa-  ko'izo/tumo'rite,futi'to/narinuru-", // 13
  "michinokuno- shino'bu/mojizuri,ta're/yue'ni-  mida're/some'nishi,wa're/narana'kuni-", // 14
  "kimigatame- ha'runo/no'niidete,wa'kana/tumu-  wa'ga/koro'modeni,yuki'wa/furitutu-", // 15
  "tachiwakare- i'nabano/yama'no,minenioru-  ma'tutoshi/ki'kaba,i'ma/kaerikon-", // 16
  "chihayaburu- kamiyomo/kikazu,tatutagawa-  karakurenaini,midukukurutowa-", // 17
  "suminoeno- kishi'ni/yorunami,yoru'/sa'eya-  yume'no/kayo'iji,hitomeyokuran-", // 18
  "naniwagata- miji'kaki/a'shino,fushinomamo-  awa'de/konoyoo,su'gu/shite'yotoya-", // 19
  "wabinureba- imahata/onaji,naniwanaru-  mio/tukushitemo,awantozoomo-", // 20
  "i'ma/ko'nnto- iisi/bakarini,nagatukino-  ariakeno/tuki'o,ma'ti/ide'turukana-", // 21
  "fu'kukarani- a'kino/kusakino,siorureba-  mube/yamakazeo,a'rasito/iuran-", // 22
  "tuki'/mireba- chi'jini/mono'koso,kanasikere-  wagami/hitotuno,a'kiniwa/aranedo-", // 23
  "konotabiwa- nusamo/toriaezu,tamukeyama-  mo'mijino/nishiki,ka'mino/manimani-", // 24
  "nanisiowaba- o-sakayamano,sanekadura-  hitoni/shirarede,kuru/yoshimogana-", // 25
  "ogurayama- mi'neno/momijiba,koko'ro/a'raba- i'ma/hitotabino,miyuki/matanan-", // 26
  "mikanohara- wakite/nagaruru,idumigawa-  itumikitoteka,ko'isi/karuran-", // 27
  "yamazatowa- fu'yuzo/sabisisa,masarikeru-  hitomemo/kusa'mo,karento/omo'eba-", // 28
  "kokoroateni- ora'baya/ora'n,hatusimono-  oki/madowaseru,shira'gikuno/hana-", // 29
  "ariakeno- turenaku/miesi,wakareyori-  akatukibakari,u'ki/monowanasi-", // 30
  "asaborake- ariakeno/tuki'to,mirumadeni-  yo'shinono/satoni,fureru/shira'yuki-", // 31
  "yamagawani- kazeno/kaketaru,sigaramiwa-  nagaremo/aenu,momiji/na'ri/ke'ri", // 32
  "hisakatano- hikarino/doke'ki,ha'runo/hi'ni-  shi'du/kokoronaku,hana'no/chiruran-", // 33
  "tareokamo- siru/hitonisen,takasagono-  ma'tumo/mukasino,to'mo/narana'kuni-", // 34
  "hito'waiza kokoromo/shirazu,furusatowa-  hana'zo/mukashino,ka'ni/nio'ikeru-", // 35
  "natu'no/yo'wa mada/yoinagara,akenuruo-  ku'mono/idukoni,tuki'yadoru/ra'n-", // 36
  "siratuyuni- kazeno/fukisiku,a'kino/no'wa-  turanukitomenu,tama'zo/chi'rikeru-", // 37
  "wasuraruru- mioba/omo'wazu,chika'itesi   hito'no/i'nochino,o'shikumo/arukana-", // 38
  "asaju-no- ononosinohara,sinoburedo-  amarite/na'doka,hito'no/koisiki-", // 39
  "sinoburedo- iro'ni/idenikeri,waga/ko'iwa-  monoya/omouto,hito'no/toumade-", // 40
  "koisucho- waganawa/madaki,tachinikeri-  hitoshirezukoso,omoisomesika-", // 41
  "chigi'rikina katamini/sodeo,simoritutu-  suenomatuyama,nami/kosaji/towa-", // 42
  "aimiteno- nochino/kokoroni,kurabureba-  mukasiwa/monoo,omowazarikeri-", // 43
  "aukotono- taetesinakuwa,nakanakuni-  hitoomo/miomo,uramizaramasi-", // 44
  "a'waretomo- iubeki/hitowa,omooede-  mino/itadurani,narinubekikana-", // 45
  "yuranotoo- wataru/funabito,kajiotae-  yukuemo/siranu,ko'ino/michikana-", // 46
  "yaemugura- sigereru/ya'dono,sabisikini-   hito'koso/miene,a'kiwa/kinikeri-", // 47
  "kazeo/itami- iwautu/namino,onorenomi-  kuda'kete/monoo,omoukorokana-", // 48
  "mikaki/moriejino/takuhino,yo'wa/moete   hiruwa/kietutu,monookoso/omo'e-", // 49
  "kimigatame- osikarazarisi,i'nochisae-  nagakumoganato,omoikerukana-", // 50
  "kakutodani- eyawaibukino,sasimokusa   sasimosirajina,moyuru/omo'io-", // 51
  "akenureba- kururumonotowa,sirinagara- na'o/urameshiki,asaborakekana-", // 52
  "nagekitutu- hitorinuruyono,akurumawa-  ikani/hisashiki,monotokawasiru-", // 53
  "wasurejino- yukusuemadewa,ka'takereba-  kyo'-o/kagirino,i'nochitomogana-", // 54
  "taki'no/oto'wa- ta'ete/hisa'siku/narinu'redo-  nako'so/nagarete,na'o/kiko'ekere-", // 55
  "arazaran- konoyono/hokano,omoideni- i'ma/hito'dabino,aukotomogana-", // 56
  "meguriaite- misiya/soretomo,wakanumani-  ku'moga/kurenisi,yowano/tuki'kana-", // 57
  "arimayama- inano/sakihara,kaze'/fu'keba-  idesoyohitoo,wasureyawasuru-", // 58
  "yasurawade- nenamasi/monoo,sa'yo/fu'kete-  katabu'ku/ma'deno,tuki'o/mi'sikana-", // 59
  "ohoeyama- ikunono/michino,to'okereba-  ma'da/fumi'momizu,a'mano/hashidate-", // 60
  "inisieno- na'rano/miyakono,yaeza'kura  kyo'-/kokonoeni,nioinurukana-", // 61
  "yo'o/ko'mete torino/soranewa,haka'rutomo   yo'ni/o-sakano,se'kiwayurusaji-", // 62
  "i'mawatada- omoitaenan,toba'kario   hitodutena'rade,iu/yosimogana-", // 63
  "asaborake- u'jino/kawagiri,taeda'eni-  arawa're/wata'ru se'zeno/ajirogi-", // 64
  "urami/wabi- osanu/sodeda'ni,a'rumonoo- ko'ini/kuchinan,na/ko'so/osikere-", // 65
  "morotomoni- a'wareto/omo'e,yamaza'kura   hanayori/hokani,siru/hitomo/nasi-", // 66
  "ha'runo/yo'no- yume/ba'karinaru,tamakurani-  ka'inaku/tatamu,nako'so/o'sikere-", // 67
  "kokoronimo- a'rade/ukiyoni,nagaraeba-  ko'ishikarubeki,yo'wano/tuki'kana-", // 68
  "a'rasi/fu'ku mimurono/yama'no,momijibawa  ta'tutano/kawa'no,ni'shiki/na'ri/ke'ri", // 69
  "sabishisani- ya'doo/ta'chi/i'dete,nagamu'reba-  i'dukomo/onaji,a'kino/yu-gure", // 70
  "yu-sareba- ka'dotano/i'naba,otodurete-  a'sino/maroyani,aki'kazezo/fu'ku-", // 71
  "otoni/kiku- ta'kasino/hama'no,adanamiwa-  kake'jiya/sodeno,nuremoko'sosure-", // 72
  "takasogono- o'noeno/sakura,saki'ni/ke'ri-  to'yamano/kasumi,tatazumo/aranan-", // 73
  "ukarikeru- hitoo/hatuseno,yamaorosiyo-  hagesikaretowa,inoranumono-", // 74
  "chigiriokisi- sasemoga/tu'yuo,i'nochinite-  awarekotoshino,a'kimo/inumeri-", // 75
  "watanohara- kogiidetemireba,hisakatano- kumoini/magau,okitu/shiranami-", // 76
  "se'o/haya'mi- iwa'ni/seka'ruru,taki'gawano-  waretemo/sue'ni,awa'ntozo/omo-", // 77
  "awajisima- kayou/chi'dorino,naku'/ko'eni- ikuyo/nezamenu,su'mano/sekimori-", // 78
  "aki'kazeni- tanabi'ku/ku'mono,taemayori-  moreiduru/tuki'no,ka'geno/sayakesa-", // 79
  "nagakaran- koko'romo/shirazu,kurokamino-  mida'rete/ke'sawa,mono'-/ko'so/omo'e", // 80
  "hototogisu- nakituru/katao,nagamureba-  ta'da/ariakeno,tuki'zo/noko'reru-", // 81
  "omo'i/wabi sa'temo/i'nochiwa,a'ru/monoo  u'kini/tae'nuwa,na'mida/na'ri/ke'ri", // 82
  "yononakayo- michikoso/nakere,omo'iiru-  yamano/okunimo,sikazo/nakunaru-", // 83
  "nagaraeba- mata/konogoroya,sinobaren-  usitomishiyozo,imawa/ko'ishiki-", // 84
  "yo'mo/sugara- monoomoukorowa,akeyarade- neyano/himasae,turenakarikeri-", // 85
  "nageketote- tuki'ya/hamonoo,omowasuru-  kakochigaonaru,wa'ga/na'midakana-", // 86
  "murasameno- tu'yumo/ma'da/i'nu,ma'kino/ha'ni  kiri/tachinoboru,a'kino/yu-gure", // 87
  "naniwaeno- a'sino/karineno,hitoyo/yue-  mio'/tuku'shiteya,ko'i/watarubeki-", // 88
  "tama'no/o'yo tae'naba/tae'ne,nagaraeba- sinobu'ru/kotono,yowa'rimozo/suru", // 89
  "misebayana- o'jimano/a'mano,sodeda'nimo-  nurenizonuresi,iro'wa/kawarazu", // 90
  "kirigirisu- naku'ya/shimoyono,samusironi- koromokatasiki,hitorikamonen-", // 91
  "wa'ga/sodewa sioini/mienu,okino/isino-  hitokoso/sirane,kawa'ku/mamonasi", // 92
  "yono'nakawa- tunenimo/gamona,nagisakogu-  a'mano/obuneno,tunade/kanasimo-", // 93
  "miyosinono- yamano/akikaze,sa'yo/fu'kete- furu'sato/sa'muku,koromoutunari-", // 94
  "ookenaku- ukiyono/ta'mini,ooukana-  wa'ga/ta'tu/so'mani,sumizomeno/sode", // 95
  "hana'/sasou,a'rasino/niwano,yuki/na'rade-  furiyukumonowa,wa'gami/na'ri/ke'ri", // 96
  "ko'nu/hitoo- matuhono/urano,yu-nagini-  yaku'ya/mo'shiono,mimo'/koga'retutu", // 97
  "kaze/soyo'gu- na'rano/ogawano,yu-gurewa-  misogizo/na'tuno,sirusi/na'ri/ke'ru", // 98
  "hitomooshi- hitomouramesi,ajikinaku-  yoo/omouyueni,mono/omoumiwa-", // 99
  "momoshikiya- furuki/nokibano,sinobunimo-  na'o/amariaru,mukashi/na'ri/ke'ri", // 100
  };

const int MAX = 100;
int max = 0;
int done[MAX];

void setup() {
  Serial.begin(115200);
  Serial.println("");
//Wire.begin(4, 5); // (SDA,SCL) Default
  Wire.begin(); // (SDA,SCL)

  for (int i = 0; i < MAX; i++) {
    done[i] = i;
  }

  long seed = system_adc_read() * system_adc_read();
  randomSeed(seed);

  pinMode(SENSOR_PIN, INPUT);

  delay(200);
}

void check_sensor() {
  bool isReady = digitalRead(SENSOR_PIN) == 0;
  bool onSpeak = isBusy();
  if (isReady && !onSpeak) {
    if (max == 0) max = MAX;
    int i = (int)random(max);
    int n = done[i];
    swap(done, i, --max);
    Serial.println(String("Now : ") + (MAX-max) + " No. : " + (n+1));
    delay(3000);
    speak(data[n]);
  }
  if (!isReady && onSpeak) {
    speak_stop();
  }
}

void swap(int *data, int a, int b) {
  if (a != b) {
    int tmp = data[a];
    data[a] = data[b];
    data[b] = tmp;
  }
}

void check_serial() {
  if (0 < Serial.available()) {
    delay(10);
    String buff;
    while (0 < Serial.available()) {
      char ch = Serial.read();
      if (ch == '\n') {
        speak(buff);
        Serial.println(buff);
      } else if (0x20 <= ch && ch <= 0x7f) {
        // ASCII文字コード
        buff = buff + ch;
      } else {
        // Do nothing
      }
    }
  }
}

void loop() {
//check_serial();
  check_sensor();
  delay(200);
}


bool isBusy() {
  Wire.requestFrom(ADDR, (uint8_t)1);
  while (true) {
    while (Wire.available()) {
      char b = Wire.read();
      // コマンド実行中は Busy 状態になり、この間に他のコマンドを受信すると、コマンド応答として '*' が返ります。
      // なお、発話中は Busy 状態となります。
      return b == '*';
    }
    delay(100);
  }
}

void speak_stop() {
  speak("$");
}

void speak(String buff) {

  uint8_t *data = (uint8_t *)buff.c_str();
  write_data(ADDR, data, buff.length());

  // 1つのコマンドは 1byte~127byte の ASCII 文字列の最後にデリミタとして CR(0x0D)を付与します。
  // AT30110F4 は、この CR の受信後にコマンドを実行します。なお、コマンドの大文字、小文字は区別されます。

  uint8_t cmd[] = {0x0D};
  write_data(ADDR, cmd, 1);

  delay(1);
}

/////////

void write_data(uint8_t addr, uint8_t *data, size_t len) {
  // wireライブラリは最大32バイトなので分割して送信する
  const size_t max = 32;
  size_t n = len / max;
  size_t m = len % max;
  size_t i = 0;
  for (; i < n; i++) {
    write(addr, &data[i*max], max);
  }
  write(addr, &data[i*max], m);
  delay(1);
}

void write(uint8_t addr, uint8_t *data, size_t len) {
  Wire.beginTransmission(addr);
  Wire.write(data, len);
  Wire.endTransmission();
  delayMicroseconds(27); // 26.3us
}

完成

IMG_2634.JPG

整理してない回路で、すいません。

  • 上段 : WROOM02
    磁石スイッチ を 13番ピン につなげてます
  • 中段 : 音声合成LSI ATP3011
  • 下段 : オーディオパワーアンプIC LM380N の増幅回路
    増幅回路はアンプIC使っても、けっこう面倒でした。
    ラジカセ(死語?) にでも繋げたほうが、楽ですね。

おわりに

  • 棒読み感は、ぬぐえません。しかたない。
  • 発話ICはとても簡単に使えるので、今度、ハッカソンで使ってやろう。
  • 余談ですが、たまたま秋月でみつけた オーディオパワーアンプIC LM380N は超有名なICのようです。
    「革命アンプ」「非革命アンプ」という回路が考案されており、負帰還をかけることで 120円 とは思えぬ音質を引き出せるそう。
    http://homepage2.nifty.com/zro/Experiment/LM380-6/index.html
    この IC は、もう製造中止になるそうで、非革命を体験したい人は早めに買いだめしとくべし。
    今回は、標準的な使い方でした。
  • おい!読み間違っとるよ! という部分は、すぐ教えてください! 恥ずかしいっす。。

参考資料

exabugs
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away