Interactive Project- Andrew Wang and Daniel Hernandez

For our interactive project, Daniel and I decided to create a Donald Trump voodoo doll. When you picked him up, he would speak one of his lines stored in the SD card in the QT PY. The code would detect when a charge (human touch) would interact with the “SDA” pin on the QT PY.

Image Board (for easier access)

Materials:

QT PY + Audio BFF + SD Card

Trump Plush

Speaker

Battery Pack

Tape

Step-by-step Guide:

Step 1: Upload desired .wav files onto the SD card (formatted as FAT32)

Step 2: Upload code onto the QT PY

Step 3: Solder the Audio BFF and QT PY together, and slot the SD card in

Step 4: Solder the battery pack onto the QT PY and connect the speaker onto the Audio BFF

Step 5: Hollow out the Plush of stuffing to place the full electronic combo inside, with the QT PY with the “SDA” pin facing the outside

Step 6: Close the plush with tape (sewing kit or other closure method)!

Code:

// QT Py ESP32 Pico + Audio BFF — minimal WAV sequencer

// Plays /track1.wav .. /track5.wav in order, 2 sec gap in between, then loops.

// Requires: WAV audio files, PCM 16-bit, stereo, 44.1 kHz with a 44-byte header.

// SD card must be formatted as FAT32 – not ExFAT. ExFAT wont work.

#include <Arduino.h>

#include <SPI.h>

#include <SD.h>

#include “driver/i2s.h”

// —- Pin assignments on QTPY using Audio BFF defaults —-

#define SD_CS A0

#define I2S_DOUT A1

#define I2S_LRC A2

#define I2S_BCLK A3

// —- I2S setup (fixed file type: 44.1 kHz, 16-bit, stereo) —-

static const i2s_port_t I2S_PORT = I2S_NUM_0;

static 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_RIGHT_LEFT,

.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),

.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,

.dma_buf_count = 8,

.dma_buf_len = 256,

.use_apll = false,

.tx_desc_auto_clear = true,

.fixed_mclk = -1

};

static i2s_pin_config_t i2s_pins = {

.bck_io_num = I2S_BCLK,

.ws_io_num = I2S_LRC,

.data_out_num = I2S_DOUT,

.data_in_num = I2S_PIN_NO_CHANGE

};

// —- sound stream buffer —-

uint8_t buf[1024]; // multiple of 4 bytes (stereo 16-bit frames)

// —- Play one audio file at a time, w/ 2 sec delay in between

void playWavFixedFormat(const char* path) {

File f = SD.open(path, FILE_READ);

if (!f) {

Serial.print(“Missing file: “); Serial.println(path);

return;

}

// Skip the 44-byte WAV header

if (!f.seek(44)) {

Serial.print(“Seek failed (header) for “); Serial.println(path);

f.close();

return;

}

Serial.print(“Playing “); Serial.println(path);

while (true) {

size_t n = f.read(buf, sizeof(buf));

if (n == 0) break; // EOF

size_t written = 0; // Write whole chunk to I2S (blocking)

if (i2s_write(I2S_PORT, (const char*)buf, n, &written, portMAX_DELAY) != ESP_OK) {

Serial.println(“i2s_write error”);

break;

}

if (written != n) { // Shouldn’t happen with portMAX_DELAY, but bail if it does

Serial.println(“Short write to I2S”);

break;

}

}

f.close();

}

void setup() {

Serial.begin(115200);

while (!Serial) { delay(10); }

Serial.println(“QT Py ESP32 Pico + Audio BFF Player”);

pinMode(SD_CS, OUTPUT); // —- SD init —-

digitalWrite(SD_CS, HIGH);

SPI.begin();

if (!SD.begin(SD_CS, SPI, 10000000)) { // slightly slower SPI can help some cards/boards; 10 MHz here

Serial.println(“SD begin failed. Make sure SD card is MS-DOS (FAT)/FAT32 and CS=A0.”);

for(;;) delay(1000);

}

Serial.println(“SD OK.”);

if (i2s_driver_install(I2S_PORT, &i2s_config, 0, nullptr) != ESP_OK) { // —- I2S init —-

Serial.println(“i2s_driver_install failed”);

for(;;) delay(1000);

}

if (i2s_set_pin(I2S_PORT, &i2s_pins) != ESP_OK) {

Serial.println(“i2s_set_pin failed”);

for(;;) delay(1000);

}

// Confirm clock (optional, redundant because sample_rate already set)

i2s_set_clk(I2S_PORT, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);

}

void loop() {

for (int i = 1; i <= 5; i++) { // Play track1 – track5 with 2-second gaps

char path[16];

snprintf(path, sizeof(path), “/track%d.wav”, i);

playWavFixedFormat(path);

delay(2000); // 2 sec gap

}

// Loop forever

}