Interested_Spresenseのアイコン画像
Interested_Spresense 2022年09月26日作成 (2022年09月26日更新) © CC BY 4+
製作品 製作品 閲覧数 434
Interested_Spresense 2022年09月26日作成 (2022年09月26日更新) © CC BY 4+ 製作品 製作品 閲覧数 434

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

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

はじめに

ランニングしているとどうしても自分に負けて途中でやめてしまいがちな自分に向けて、ちゃんと走らないと怒ってくれるウォークマンを作りました。

Spresense は、小型で簡単に音楽生成ができ、かつ、AIを取り込めるので、自分のランニング状況に合わせて再生音声を変更するということが可能です。それを利用しました。

用意したもの

キャプションを入力できます
SPRESENSEメインボード[CXD5602PWBMAIN1]

PDA01
B-stem PDA01 SPRESENSE用機能拡張ボード

PDA01は、小型でオーディオ出力を持っているだけでなく、充電機能、加速度センサもついているので、今回のケースにはピッタリ。

後は、SDカード。SDカードは何でもよいのですが、キオクシアのモノが安定動作しています。

キャプションを入力できます
KIOXIA(キオクシア) 旧東芝メモリ microSDHCカード 32GB

準備したものはこれだけ。

元ネタ

元ネタはこれです。
http://spresense.livedoor.blog/archives/24443523.html

プログラム

Spresenseには、Step Countorのサンプルプログラムがあり、そこには行動認識のAIも入っています。

4.1. Step Counter を使ってみよう

ただ、対応センサが BMI160 になのですが、PDA01 に載っているのは、KX122 なのでそちらへの変更を行います。

変更の仕方は、以下にあります。

4.1.2. Step Counter の物理センサを変更してみる

これによって、走ってる、歩いてる、止まってるの行動認識ができるようになります。
これを利用して走っていないことを認識したら怒るようにします。

次に、音声再生を行います。せっかくなのでちゃんと聞きたい音楽を聴こうということで、
プレイリストでの音声再生を行うスケッチを利用します。

1.2.5. プレイリスト再生

これを StepCounter のスケッチにマージします。

ただ、再生する音声は、ちゃんと走っている時と、歩いてしまっている時で切り替えたいので、Playlistを切り替えて再生するようにしてみます。

実際には、Playlist の インスタンスを3つ生成し、実際に使うリストへのポイントをきりかえることで実現します。

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します。

ここまでで、プログラムの準備ができました。

音声ファイル

各音声ファイルとそのプレイリストは、適宜、準備をします。
聴きたい曲だけでなく、子供たちから、歩いてしまった、止まってしまったときのお怒りの声を頂きました!

ケーシングと動作

実際に、良いケースがなかったので、子供が食べていたチョコボールの箱に入れてみました。

キャプションを入力できます

動かしている動画は、取れなかったので、今度追記します。

終わりに

もうちょっと丁寧にやっていこうと思ったのですが、締め切りに迫られざっくりになってしまいました。
今後、もうちょっと整理していきます。

ログインしてコメントを投稿する