Hardware components | ||||||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
![]() |
|
@youtoy taught me on X that no matter how difficult it is, I should never give up.
So, even though the deadline was approaching, I started working on the piece.
It didn't turn out exactly as I had imagined, but it turned out cute.
I wanted to create a piece that expressed my gratitude.
In Japanese, “thank you” is pronounced “arigatou, ” which sounds similar to “alligator, ” so I created a piece where an alligator says “thank you.”
I designed the 3D model using Fusion and printed it using a BambuLab P1S 3D printer.
When you touch the green tongue, an audio file saved on an SD card plays. During playback, the tongue turns red.
That's all there is to it.
Alligato-R
>
alligato-r.ino
ArduinoPressing the green button will load the audio files stored on the SD card. The button will turn red while loading.
#include <M5Core2.h>
#include <driver/i2s.h>
void setup() {
M5.begin();
// 画面を左に90度回転(横向き)
M5.Lcd.setRotation(1);
M5.Lcd.setTextSize(3);
M5.Lcd.fillScreen(BLACK);
// スピーカー有効化
M5.Axp.SetSpkEnable(true);
// SDカード初期化
if (!SD.begin()) {
M5.Lcd.setTextColor(RED);
M5.Lcd.println("SD Card Error!");
delay(2000);
}
// I2S設定(スピーカー用)
initSpeaker();
// ボタン表示
drawButtons();
}
void initSpeaker() {
i2s_driver_uninstall(I2S_NUM_0);
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = 44100,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 64,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_pin_config_t pin_config = {
.bck_io_num = 12,
.ws_io_num = 0,
.data_out_num = 2,
.data_in_num = I2S_PIN_NO_CHANGE
};
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
pin_config.mck_io_num = I2S_PIN_NO_CHANGE;
#endif
i2s_set_pin(I2S_NUM_0, &pin_config);
}
void drawButtons() {
M5.Lcd.fillScreen(BLACK);
// 大きなありがとうボタン(画面中央に配置)
M5.Lcd.fillRoundRect(60, 60, 200, 120, 15, GREEN);
}
void playThanksAudio() {
// 画面にフィードバック表示(シンプルに)
M5.Lcd.fillRoundRect(60, 60, 200, 120, 15, RED);
if (SD.exists("/arigato_ja.wav")) {
File audioFile = SD.open("/arigato_ja.wav");
if (audioFile) {
// WAVヘッダーをスキップ
audioFile.seek(44);
uint8_t buffer[512];
size_t bytes_written;
while (audioFile.available()) {
size_t bytesRead = audioFile.read(buffer, sizeof(buffer));
if (bytesRead > 0) {
i2s_write(I2S_NUM_0, buffer, bytesRead, &bytes_written, portMAX_DELAY);
}
}
audioFile.close();
}
} else {
// WAVファイルがない場合はビープ音
playBeep();
}
// 1秒後にボタンを元に戻す
delay(1000);
drawButtons();
}
void playBeep() {
// シンプルなビープ音
int16_t high = 8000;
int16_t low = -8000;
size_t bytes_written;
for (int i = 0; i < 22050; i++) { // 0.5秒
int16_t sample = (i % 44) < 22 ? high : low; // ~1000Hz
i2s_write(I2S_NUM_0, &sample, sizeof(sample), &bytes_written, portMAX_DELAY);
}
}
void showInfo() {
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(CYAN);
M5.Lcd.setCursor(50, 30);
M5.Lcd.println("System Info");
M5.Lcd.setTextSize(1);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(10, 70);
M5.Lcd.println("Audio File: /arigato_ja.wav");
M5.Lcd.setCursor(10, 90);
if (SD.exists("/arigato_ja.wav")) {
File f = SD.open("/arigato_ja.wav");
M5.Lcd.println("Status: Found (" + String(f.size()) + " bytes)");
f.close();
} else {
M5.Lcd.setTextColor(RED);
M5.Lcd.println("Status: Not Found");
M5.Lcd.setTextColor(WHITE);
}
M5.Lcd.setCursor(10, 110);
M5.Lcd.println("Sample Rate: 44.1kHz");
M5.Lcd.setCursor(10, 130);
M5.Lcd.println("Channels: Mono");
M5.Lcd.setCursor(10, 150);
M5.Lcd.println("Bits: 16-bit");
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(10, 190);
M5.Lcd.println("Touch anywhere to return");
// タッチ待ち
while (true) {
M5.update();
if (M5.Touch.ispressed() || M5.BtnA.wasPressed() || M5.BtnB.wasPressed() || M5.BtnC.wasPressed()) {
drawButtons();
break;
}
delay(50);
}
}
void loop() {
M5.update();
// タッチ検出(ありがとうボタンエリア: x:60-260, y:60-180)
if (M5.Touch.ispressed()) {
TouchPoint_t pos = M5.Touch.getPressPoint();
if (pos.x >= 60 && pos.x <= 260 && pos.y >= 60 && pos.y <= 180) {
playThanksAudio();
delay(200); // デバウンス
}
}
// 物理ボタン
if (M5.BtnA.wasPressed()) {
// テスト再生
playThanksAudio();
}
if (M5.BtnB.wasPressed()) {
// 画面リロード
drawButtons();
}
if (M5.BtnC.wasPressed()) {
// 情報表示
showInfo();
}
delay(50);
}
Comments