編集履歴一覧に戻る
Interested_Spresenseのアイコン画像

Interested_Spresense が 2022年09月26日04時37分00秒 に編集

初版

タイトルの変更

+

ちゃんと走らないと怒られるウォークマン

タグの変更

+

spresense

記事種類の変更

+

製作品

ライセンスの変更

+

(CC BY 4+) Creative Commons Attribution CC BY version 4.0 or later

本文の変更

+

# はじめに ランニングしているとどうしても自分に負けて途中でやめてしまいがちな自分に向けて、ちゃんと走らないと怒ってくれるウォークマンを作りました。 Spresense は、小型で簡単に音楽生成ができ、かつ、AIを取り込めるので、自分のランニング状況に合わせて再生音声を変更するということが可能です。それを利用しました。 # 用意したもの ![キャプションを入力できます](https://camo.elchika.com/0d03bda1389caa266e71b04318ba92e14f89c7e1/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f35313935656231352d393966302d346438342d383363652d6562366533343062303533642f36323134346362302d323533362d343232372d396231362d626336653765633333366262/) [SPRESENSEメインボード[CXD5602PWBMAIN1]](https://www.switch-science.com/catalog/3900/) ![PDA01](https://camo.elchika.com/1ab6278ec31e9a7191182a57d252b08bf1bbcdcc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f35313935656231352d393966302d346438342d383363652d6562366533343062303533642f30656537323163612d396561382d346434612d613865662d313231386236383766363730/) [B-stem PDA01 SPRESENSE用機能拡張ボード](https://p-art.net/pda01-2) PDA01は、小型でオーディオ出力を持っているだけでなく、充電機能、加速度センサもついているので、今回のケースにはピッタリ。 後は、SDカード。SDカードは何でもよいのですが、キオクシアのモノが安定動作しています。 ![キャプションを入力できます](https://camo.elchika.com/97ee19818ce798770e2243b93e5d939762047b3b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f35313935656231352d393966302d346438342d383363652d6562366533343062303533642f33653630366635372d343830352d346463612d623631382d323765353333663937663536/) [KIOXIA(キオクシア) 旧東芝メモリ microSDHCカード 32GB](https://www.amazon.co.jp/dp/B08PTNWQ6P?aaxitk=a8f1b316576d3af7dc8db832d10d5cd9&smid=AN1VRQENFRJN5&ref=dacx_dp_7467415030103_8217637380303&th=1) 準備したものはこれだけ。 # 元ネタ 元ネタはこれです。 http://spresense.livedoor.blog/archives/24443523.html # プログラム Spresenseには、Step Countorのサンプルプログラムがあり、そこには行動認識のAIも入っています。 [4.1. Step Counter を使ってみよう](https://developer.sony.com/develop/spresense/docs/arduino_tutorials_ja.html#_step_counter_%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86) ただ、対応センサが [BMI160](https://www.bosch-sensortec.com/products/motion-sensors/imus/bmi160/) になのですが、[PDA01](https://p-art.net/pda01-2) に載っているのは、[KX122](https://www.mouser.jp/ProductDetail/Kionix/KX122-1037?qs=6OIXIgk3fwdhApAbA9esZQ%3D%3D) なのでそちらへの変更を行います。 変更の仕方は、以下にあります。 [4.1.2. Step Counter の物理センサを変更してみる](https://developer.sony.com/develop/spresense/docs/arduino_tutorials_ja.html#_step_counter_%E3%81%AE%E7%89%A9%E7%90%86%E3%82%BB%E3%83%B3%E3%82%B5%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B) これによって、走ってる、歩いてる、止まってるの行動認識ができるようになります。 これを利用して走っていないことを認識したら怒るようにします。 次に、音声再生を行います。せっかくなのでちゃんと聞きたい音楽を聴こうということで、 プレイリストでの音声再生を行うスケッチを利用します。 [1.2.5. プレイリスト再生](https://developer.sony.com/develop/spresense/docs/arduino_tutorials_ja.html#_%E3%83%97%E3%83%AC%E3%82%A4%E3%83%AA%E3%82%B9%E3%83%88%E5%86%8D%E7%94%9F) これを StepCounter のスケッチにマージします。 ただ、再生する音声は、ちゃんと走っている時と、歩いてしまっている時で切り替えたいので、Playlistを切り替えて再生するようにしてみます。 実際には、Playlist の インスタンスを3つ生成し、実際に使うリストへのポイントをきりかえることで実現します。 ```playlistの生成 Playlist thePlaylist0("TRACK_DB0.CSV"); Playlist thePlaylist1("TRACK_DB1.CSV"); Playlist thePlaylist2("TRACK_DB2.CSV"); Playlist* thePlaylist = &thePlaylist0; ``` ```変化があったかどうかを伝える変数 static uint8_t ChangeList = 0xff; ``` とし、それぞれの関数 ``` static void list() static bool next() static bool prev() ``` の中の ``` thePlaylist. ``` を ``` thePlaylist-> ``` に変更。 ``` void setup() ``` の中の ``` thePlaylist. ``` を ``` thePlaylist0. thePlaylist1. thePlaylist2. ``` に変更し、行動の変化点を検出したら切り替えるように、行動認識からの結果を判定する ``` bool step_counter_result(sensor_command_data_mh_t &data) ``` の中に、 ``` static uint current_type = STEP_COUNTER_MOVEMENT_TYPE_STILL; ``` を定義し、 ``` if(current_type != steps->movement_type){ switch (steps->movement_type) { case STEP_COUNTER_MOVEMENT_TYPE_WALK: ChangeList = 1; break; case STEP_COUNTER_MOVEMENT_TYPE_RUN: ChangeList = 2; break; case STEP_COUNTER_MOVEMENT_TYPE_STILL: ChangeList = 0; break; default: ChangeList = 0xff; break; } current_type = steps->movement_type; } ``` と言った感じで、状態が変わった場合、 ChangeList を変更して再生すべきリストが変わったことを伝えます。 音楽生成部分では、文字をscanfで入力する部分を削除し、 ``` if (Serial.available() > 0) { switch (Serial.read()) { case 'a': // autoplay if (preset.autoplay) { preset.autoplay = 0; } else { preset.autoplay = 1; } printf("Auto=%s\n", (preset.autoplay) ? "On" : "Off"); EEPROM.put(eeprom_idx, preset); break; case 'p': // play if (s_state == Stopped) { s_state = Ready; show(&currentTrack); } break; case 's': // stop if (s_state == Active) { stop(); } s_state = Stopped; break; case '+': // volume up preset.volume += 10; if (preset.volume > 120) { /* set max volume */ preset.volume = 120; } printf("Volume=%d\n", preset.volume); theAudio->setVolume(preset.volume); EEPROM.put(eeprom_idx, preset); break; case '-': // volume down preset.volume -= 10; if (preset.volume < -1020) { /* set min volume */ preset.volume = -1020; } printf("Volume=%d\n", preset.volume); theAudio->setVolume(preset.volume); EEPROM.put(eeprom_idx, preset); break; case 'l': // list if (preset.repeat) { thePlaylist.setRepeatMode(Playlist::RepeatModeOff); list(); thePlaylist.setRepeatMode(Playlist::RepeatModeOn); } else { list(); } break; case 'n': // next if (s_state == Ready) { // do nothing } else { // s_state == Active or Stopped if (s_state == Active) { stop(); s_state = Ready; } if (!next()) { s_state = Stopped; } } break; case 'b': // back if (s_state == Active) { stop(); s_state = Ready; } prev(); break; case 'r': // repeat if (preset.repeat) { preset.repeat = 0; thePlaylist.setRepeatMode(Playlist::RepeatModeOff); } else { preset.repeat = 1; thePlaylist.setRepeatMode(Playlist::RepeatModeOn); } printf("Repeat=%s\n", (preset.repeat) ? "On" : "Off"); EEPROM.put(eeprom_idx, preset); break; case 'R': // random if (preset.random) { preset.random = 0; thePlaylist.setPlayMode(Playlist::PlayModeNormal); } else { preset.random = 1; thePlaylist.setPlayMode(Playlist::PlayModeShuffle); } printf("Random=%s\n", (preset.random) ? "On" : "Off"); EEPROM.put(eeprom_idx, preset); break; case 'm': case 'h': case '?': menu(); break; default: break; } } ``` その代わりに、リストの変更要求が来たら、変更するコードに変えます。 ``` switch (ChangeList) { case 1: // change list if (s_state == Active) { stop(); s_state = Ready; } thePlaylist = &thePlaylist1; puts("Chaneg to 1"); next(); ChangeList=0xff; break; case 2: // change list if (s_state == Active) { stop(); s_state = Ready; } thePlaylist = &thePlaylist2; next(); puts("Chaneg to 2"); ChangeList=0xff; break; case 0: // change list if (s_state == Active) { stop(); s_state = Ready; } thePlaylist = &thePlaylist0; next(); puts("Chaneg to 0"); ChangeList=0xff; break; default: break; } ``` これによって、動作すると再生リストが変わるコードができます。 ※作成したコードは整理した上、どこかにUPします。 ここまでで、プログラムの準備ができました。 # 音声ファイル 各音声ファイルとそのプレイリストは、適宜、準備をします。 聴きたい曲だけでなく、子供たちから、歩いてしまった、止まってしまったときのお怒りの声を頂きました! # ケーシングと動作 実際に、良いケースがなかったので、子供が食べていたチョコボールの箱に入れてみました。 ![キャプションを入力できます](https://camo.elchika.com/f66fb7dca910fa6b36b73e519b540070e2e10702/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f35313935656231352d393966302d346438342d383363652d6562366533343062303533642f63646465376663632d663535322d343534612d623966392d373762663135656636363930/) 動かしている動画は、取れなかったので、今度追記します。 # 終わりに もうちょっと丁寧にやっていこうと思ったのですが、締め切りに迫られざっくりになってしまいました。 今後、もうちょっと整理していきます。