導電布で作る しゃべるぬいぐるみの服

ハードウェアの構成

ScreenShot 2020-12-15 19.49.47.png

nünoはGROVE端子に対応しているのでGROVEケーブルで接続するだけで作成できます。

Arduino

Arduinoのプログラムは下記です。
また、 nünoではMTCH6102という静電タッチセンサをつかっているので、
プログラムと同じ階層にこちらから借りてきた

  • MTCH6102.h
  • MTCH6102.cpp
    を利用しています
#include <M5Core2.h>
#include <Arduino.h>
#include <Wire.h>
#include "MTCH6102.h"
#include <driver/i2s.h>


#define ADDR 0x25
#define ScreenWidth 320
#define ScreenHeight 240

//Voice
#define CONFIG_I2S_BCK_PIN 12
#define CONFIG_I2S_LRCK_PIN 0
#define CONFIG_I2S_DATA_PIN 2
#define CONFIG_I2S_DATA_IN_PIN 34

#define Speak_I2S_NUMBER I2S_NUM_0

#define MODE_MIC 0
#define MODE_SPK 1
#define DATA_SIZE 1024

extern const unsigned char nani[13890];
extern const unsigned char kusuguttai[28406];
extern const unsigned char konnichiwa[14922];
extern const unsigned char doushitano[15824];

//Voide end

MTCH6102 mtch = MTCH6102();

const int len = 8;//感知ポイント数
int nuno_mode = 1;

void setup() {

  // Initialize the M5Stack object
  M5.begin();
  M5.Lcd.fillScreen(TFT_BLACK);
  Serial.begin(115200);

  //Wire.begin();
  mtch.begin(ADDR);
  delay(100);
  mtch.writeRegister(MTCH6102_NUMBEROFXCHANNELS, 0x07);
  mtch.writeRegister(MTCH6102_NUMBEROFYCHANNELS, 0x03);//最低3点必要なため
  mtch.writeRegister(MTCH6102_MODE, MTCH6102_MODE_FULL);
  mtch.writeRegister(MTCH6102_HORIZONTALSWIPEDISTANCE, 0x04);
  mtch.writeRegister(MTCH6102_MINSWIPEVELOCITY, 0x02);
  mtch.writeRegister(MTCH6102_TAPDISTANCE, 0x02);
  mtch.writeRegister(MTCH6102_BASEPOSFILTER, 0x00);
  mtch.writeRegister(MTCH6102_BASENEGAFILTER, 0x00);

  mtch.writeRegister(MTCH6102_CMD, 0x20);
  delay(500);

  //------Voice
  SpeakInit();
  delay(100);
}


bool sound = 0;
void loop() {
  M5.update();
  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setCursor(0, 70);
  byte data;
  int sensVals[len];
  for (int i = 0; i < len; i++) {
    data = mtch.readRegister(MTCH6102_SENSORVALUE_RX0 + i);
    sensVals[i] = data;
    M5.Lcd.fillRect(30 + (i * 35), ScreenHeight - 20, 30, 10, TFT_BLACK);
    M5.Lcd.setCursor(30 + (i * 35), ScreenHeight - 20);
    M5.Lcd.print(data);
  }

  if (nuno_mode == 1) {
    data = mtch.readRegister(MTCH6102_GESTURESTATE);
    if (data != 0) {
      //Serial.println(data, HEX);
      if (data == 0x41) {
        Serial.println("swipeLeft");
        if(sound) {DingDong("kusuguttai");}
      }
      if (data == 0x61) {
        Serial.println("swipeRight");
        if(sound) {DingDong("doushitano");}
      }
      if (data == 0x10) {
        Serial.println("click");
        if(sound) {DingDong("nani");}
      }
      if (data == 0x20) {
        Serial.println("double click");
        if(sound) {DingDong("konnichiwa");}
      }
    }
  }

  M5.Lcd.setCursor(0, 70);

  //背景ライン
  for (int i = 0; i < len; i++) {
    M5.Lcd.drawLine((i + 1) * 35, ScreenHeight - 40, (i + 1) * 35, 0, 0x0000cc);
  }
  for (int i = 1; i < 11; i++) {
    M5.Lcd.drawLine(0, i * 20, ScreenWidth, i * 20, 0x0000cc);
  }
  //グラフ線の描画
  for (int i = 0; i < len + 1; i++) {
    float prev = 0;
    float current = 0;
    if (i == 0) {
      prev = 0;
    } else {
      prev = sensVals[i - 1];
    }
    if (i == len) {
      current = 0;
    } else {
      current = sensVals[i];
    }
    M5.Lcd.drawLine(i * 35, 200 - (prev / 255) * 200, ((i + 1) * 35), 200 - (current / 255) * 200, TFT_WHITE);
  }

  delay(100);
}


//-------Voice

bool InitI2SSpeakOrMic(int mode)
{
  esp_err_t err = ESP_OK;

  i2s_driver_uninstall(Speak_I2S_NUMBER);
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER),
    .sample_rate = 8000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB
    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 2,
    .dma_buf_len = 128,
  };
  i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
  i2s_config.use_apll = false;
  i2s_config.tx_desc_auto_clear = true;

  err += i2s_driver_install(Speak_I2S_NUMBER, &i2s_config, 0, NULL);
  i2s_pin_config_t tx_pin_config;

  tx_pin_config.bck_io_num = CONFIG_I2S_BCK_PIN;
  tx_pin_config.ws_io_num = CONFIG_I2S_LRCK_PIN;
  tx_pin_config.data_out_num = CONFIG_I2S_DATA_PIN;
  tx_pin_config.data_in_num = CONFIG_I2S_DATA_IN_PIN;
  err += i2s_set_pin(Speak_I2S_NUMBER, &tx_pin_config);
  err += i2s_set_clk(Speak_I2S_NUMBER, 8000, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);

  return true;
}

void SpeakInit(void) {
  M5.Axp.SetSpkEnable(true);
  InitI2SSpeakOrMic(MODE_SPK);
}

void DingDong(String str)
{
  size_t bytes_written = 0;
  if(str == "nani"){
    i2s_write(Speak_I2S_NUMBER, nani, 13890, &bytes_written, portMAX_DELAY);
  }
  if(str == "kusuguttai"){
    i2s_write(Speak_I2S_NUMBER, kusuguttai, 28406, &bytes_written, portMAX_DELAY);
  }
  if(str == "konnichiwa"){
     i2s_write(Speak_I2S_NUMBER, konnichiwa, 14922, &bytes_written, portMAX_DELAY);
  }
  if(str == "doushitano"){
    i2s_write(Speak_I2S_NUMBER, doushitano, 15824, &bytes_written, portMAX_DELAY);
  }
}

//--------Voice end

音声変換

「しゃべるぬいぐるみ」なので当然音声データが必要です。
今回はArduinoのプログラム前半に

extern const unsigned char kusuguttai[28406];
extern const unsigned char konnichiwa[14922];
extern const unsigned char doushitano[15824];

という部分があるのですが、ここで外部テキストファイル化した音声データを読み込んでいます。
こちらは下記のページを参考にさせていただきました

おおまかな流れとしては

  • Audacityをインストール
  • Audacityでモノラルデータに
  • Audacityで8000Hzに変換
  • オーディオ書き出しでraw形式のデータに変換
  • コマンドラインでテキストデータに変換&クリップボードにコピー
  • Arduinoのプログラムファイル同じディレクトリに配置

という形で読み込めるようにしました。

できたもの

この動画のように触ると喋ってくれるようになりました

pic.twitter.com/MI4lYMhGlS— elevendots (@elevendots1) December 15, 2020