ハードウェアの構成

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