#include #include #include #include #include #include // I2S configuration #define I2S_NUM I2S_NUM_0 #define I2S_BCK_IO 2 // I2S clock pin #define I2S_WS_IO 0 // I2S word select (LRCK) pin #define I2S_DO_IO 1 // I2S data out pin #define I2S_DI_IO -1 // Not used //to enable or disable amp int sd_pin = 3; // SD card configuration int sck = 19; int miso = 21; int mosi = 20; int cs = 18; const int distanceThreshold = 750; int sleep_miliseconds = 2750; const char* folderName = "/birdsong/"; std::vector wavFiles; // Cache for WAV files // Create an instance of the VL53L0X sensor Adafruit_VL53L0X lox = Adafruit_VL53L0X(); void setupI2S() { i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), .sample_rate = 16000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format = I2S_COMM_FORMAT_I2S_MSB, .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_pin_config_t pin_config = { .bck_io_num = I2S_BCK_IO, .ws_io_num = I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO }; i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); i2s_set_pin(I2S_NUM, &pin_config); i2s_zero_dma_buffer(I2S_NUM); } void setupDistance() { Serial.println("Initializing VL53L0X...\n"); delay(200); // Initialize I2C communication Wire.begin(6, 7); // Initialize the VL53L0X sensor if (!lox.begin()) { Serial.println("Failed to initialize VL53L0X sensor! Check connection."); while (1) ; } Serial.println("VL53L0X Initialized Successfully."); } bool setupSDCard() { SPI.begin(sck, miso, mosi, cs); if (!SD.begin(cs, SPI)) { Serial.println("Failed to initialize SD card."); return false; } Serial.println("SD card initialized."); return true; } void listWavFilesFromMemory() { const char* birdSongs[] = { "96707__juskiddink__birdsong-beside-stream.wav", "54532__juskiddink__freesound-nightingale.wav", "56165__juskiddink__z-finland-birdsong.wav", "56301__juskiddink__nightingale-2.wav", "57620__juskiddink__dawn-chorus.wav", "67759__juskiddink__magpie.wav", "69330__juskiddink__birdsong-march.wav", "72560__juskiddink__lapwing.wav", "72905__juskiddink__seagulls-and-jackdaws.wav", "72906__juskiddink__nightingales-frogs-and-ducks.wav", "73208__juskiddink__birdsongthunderstart-of-rain.wav", "73612__juskiddink__nightingale-june.wav", "73613__juskiddink__nightingale-and-seagulls.wav", "96377__juskiddink__rooks.wav", "98480__juskiddink__birdsong-may.wav", "98629__juskiddink__birdsong-june.wav", "120093__juskiddink__birdsong-may5.wav", "121813__juskiddink__swallows-nesting.wav", "122699__juskiddink__swallow-at-dawn.wav", "125936__juskiddink__swallows-nesting-2.wav", "127536__juskiddink__nightingale4fs2.wav", "144164__juskiddink__waves-and-birdsong.wav", "157536__juskiddink__woodland-birdsong-june.wav", "157594__juskiddink__woodpecker-nest.wav", "656507__klankbeeld__eurasian-nuthatch-boomklever-zegenwerp-1403-221029_0537.wav" }; size_t birdSongsCount = sizeof(birdSongs) / sizeof(birdSongs[0]); for (size_t i = 0; i < birdSongsCount; ++i) { wavFiles.push_back(String(folderName) + String(birdSongs[i])); Serial.println("Added to vector: " + String(birdSongs[i])); } Serial.printf("Total WAV files in vector: %d\n", wavFiles.size()); } void listWavFilesFromSD(const char* folderPath) { File root = SD.open(folderPath); if (!root || !root.isDirectory()) { Serial.println("Failed to open folder or folder does not exist."); return; } File file = root.openNextFile(); while (file) { if (!file.isDirectory() && String(file.name()).endsWith(".wav") && !String(file.name()).startsWith(".")) { wavFiles.push_back(folderPath + String(file.name())); Serial.println("Cached file: " + String(file.name())); } file = root.openNextFile(); } Serial.printf("Total WAV files cached: %d\n", wavFiles.size()); } String getRandomWavFile() { if (wavFiles.empty()) { Serial.println("No WAV files cached."); return ""; } int randomIndex = rand() % wavFiles.size(); return wavFiles[randomIndex]; } void playWavFile(const char* filename) { //Disable amp pinMode(sd_pin, OUTPUT); digitalWrite(sd_pin, LOW); // Initialize I2S setupI2S(); File wavFile = SD.open(filename); Serial.println(filename); if (!wavFile) { Serial.println("Failed to open WAV file."); return; } // Skip WAV header (44 bytes for standard PCM) wavFile.seek(44); uint8_t buffer[1024]; size_t block = 0; size_t block_start = 3; while (wavFile.available()) { size_t bytesRead = wavFile.read(buffer, sizeof(buffer)); size_t bytesWritten = 0; size_t i2s_bytes_written = 0; while (bytesWritten < bytesRead) { esp_err_t err = i2s_write(I2S_NUM, buffer + bytesWritten, bytesRead - bytesWritten, &i2s_bytes_written, portMAX_DELAY); //to avoid pop only start after x audio blocks if(block == block_start) digitalWrite(sd_pin, HIGH); if (err == ESP_OK) { bytesWritten += i2s_bytes_written; } else { Serial.printf("I2S write error: %d\n", err); break; } } block ++; } wavFile.close(); digitalWrite(sd_pin, LOW); Serial.println("Finished playing WAV file."); } void lightSleep(int milliseconds) { // Configure and enter light sleep Serial.println("Entering light sleep..."); esp_sleep_enable_timer_wakeup(milliseconds * 1000); // 10 seconds in microseconds esp_light_sleep_start(); } void deepSleep(int milliseconds) { // Configure and enter light sleep Serial.println("Entering light sleep..."); esp_sleep_enable_timer_wakeup(milliseconds * 1000); // 10 seconds in microseconds esp_deep_sleep_start(); } int distance() { // Create a structure to hold the sensor data VL53L0X_RangingMeasurementData_t measure; // Perform a measurement lox.rangingTest(&measure, false); // Pass 'true' to get debug data printed to Serial int mm = 0; // Check if the measurement is valid if (measure.RangeStatus != 4) { // 4 means no valid return Serial.print("Distance: "); mm = measure.RangeMilliMeter; Serial.print(mm); Serial.println(" mm"); } else { Serial.println("Out of range"); mm = 10000; } if (mm == 0) mm = 10000; return mm; } void setupRandom(){ randomSeed(analogRead(5) + analogRead(6)); } void setupSerial(){ Serial.begin(115200); while(!Serial); } void setup() { setupSDCard(); setupRandom(); setupDistance(); // Cache WAV files in root folder //listWavFilesFromSD(folderName); listWavFilesFromMemory(); int mm = distance(); if (mm < distanceThreshold) { // Play a random WAV file String randomFile = getRandomWavFile(); if (!randomFile.isEmpty()) { playWavFile(randomFile.c_str()); } } deepSleep(sleep_miliseconds); } void loop(){ //NOOP }