// ───────────────────────────────────────────────────────────────────────────── // LED + Motor + I2S Speaker — XIAO ESP32-S3 // ───────────────────────────────────────────────────────────────────────────── // // LED filaments : GPIO7 (D8) → MOSFET gate → filaments → GND // Motor PWM : GPIO4 (D3) → MOSFET gate → motor → GND // I2S DIN : GPIO1 (D0 / A0) // I2S LRC : GPIO2 (D1 / A1) // I2S BCLK : GPIO3 (D2 / A2) // // Library required: "AudioTools" by pschatzmann // Install via: Arduino IDE → Library Manager → search "AudioTools" // ───────────────────────────────────────────────────────────────────────────── #include "AudioTools.h" // ── Pin definitions ─────────────────────────────────────────────────────────── #define LED_PIN 7 // GPIO7 — LED MOSFET gate (moved from GPIO5) #define MOTOR_PIN 4 // GPIO4 — Motor MOSFET gate #define I2S_BCLK_PIN 3 // GPIO3 #define I2S_LRC_PIN 2 // GPIO2 #define I2S_DOUT_PIN 1 // GPIO1 // ── LED breathing settings ──────────────────────────────────────────────────── const int LED_PWM_FREQ = 24989; // prime, above camera flicker range const int LED_PWM_RES = 8; // 8-bit → 0–255 const int BREATHE_MS = 4000; // full in-out cycle duration (ms) // step delay = half cycle / 255 steps const int LED_STEP_DELAY = BREATHE_MS / 2 / 255; unsigned long lastLedUpdate = 0; int brightness = 0; int ledStep = 1; // ── Motor settings ──────────────────────────────────────────────────────────── const int MOTOR_PWM_FREQ = 20000; // 20kHz — inaudible const int MOTOR_PWM_RES = 8; // 8-bit → 0–255 // ▼▼▼ Adjust motor speed here (0 = stopped, 255 = full) ▼▼▼ const int MOTOR_SPEED = 140; // ~55% — smooth constant rotation // ▲▲▲─────────────────────────────────────────────────────▲▲▲ // ── Audio settings ──────────────────────────────────────────────────────────── const int SAMPLE_RATE = 44100; const float TONE_HZ = 110.0; // A2 — low, warm fundamental tone const float TONE_VOLUME = 0.50; // 0.0–1.0, keep moderate for testing // ── AudioTools objects ──────────────────────────────────────────────────────── I2SStream i2sOut; SineWaveGenerator sineGen; GeneratedSoundStream sineStream(sineGen); VolumeStream volume(i2sOut); StreamCopy copier(volume, sineStream); AudioInfo info(SAMPLE_RATE, 2, 16); // sample rate, stereo, 16-bit // ───────────────────────────────────────────────────────────────────────────── void setupLED() { ledcAttach(LED_PIN, LED_PWM_FREQ, LED_PWM_RES); ledcWrite(LED_PIN, 0); } void setupMotor() { ledcAttach(MOTOR_PIN, MOTOR_PWM_FREQ, MOTOR_PWM_RES); ledcWrite(MOTOR_PIN, MOTOR_SPEED); } void setupAudio() { AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); // Configure I2S output auto cfg = i2sOut.defaultConfig(TX_MODE); cfg.pin_bck = I2S_BCLK_PIN; cfg.pin_ws = I2S_LRC_PIN; cfg.pin_data = I2S_DOUT_PIN; cfg.copyFrom(info); i2sOut.begin(cfg); // Volume stage auto vcfg = volume.defaultConfig(); vcfg.copyFrom(info); volume.begin(vcfg); volume.setVolume(TONE_VOLUME); // Sine generator sineGen.begin(info, TONE_HZ); sineStream.begin(info); } // ── Non-blocking LED breath ─────────────────────────────────────────────────── void updateLED() { unsigned long now = millis(); if (now - lastLedUpdate >= (unsigned long)LED_STEP_DELAY) { lastLedUpdate = now; ledcWrite(LED_PIN, brightness); brightness += ledStep; if (brightness >= 255) { brightness = 255; ledStep = -1; } if (brightness <= 0) { brightness = 0; ledStep = 1; } } } // ───────────────────────────────────────────────────────────────────────────── void setup() { Serial.begin(115200); delay(500); setupLED(); setupMotor(); setupAudio(); Serial.println("LED + Motor + Speaker running"); Serial.print("Tone: "); Serial.print(TONE_HZ); Serial.println(" Hz"); Serial.print("Motor: "); Serial.print(MOTOR_SPEED); Serial.println(" / 255"); } void loop() { copier.copy(); // feeds I2S DMA buffer — paces the loop naturally updateLED(); // non-blocking breath step }