#include #include #include #include #include #include #include #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_ADDR 0x3C #define ELECTRODE_PIN A1 #define DHT_PIN 19 #define DHT_TYPE DHT11 #define SD_CS_PIN 7 #define WAV_FILE_NAME "Sample2.wav" #define MAX_WAV_SAMPLES 480000 // 10 seconds @48kHz #define AUDIO_BLOCK_SIZE 512 #define MAX_GRAINS 20 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire); DHT dht(DHT_PIN, DHT_TYPE); SdFat sd; File wavFile; // --- WAV buffer in external SDRAM --- DSY_SDRAM_BSS int16_t wavBuffer[MAX_WAV_SAMPLES]; size_t wavSize = 0; // --- Grains --- typedef struct { float phase; float env; float freq; size_t lifespan; float fmAmount; size_t startPos; } Grain; Grain grains[MAX_GRAINS]; size_t lastGrainTime = 0; // --- Electrode sensor averaging --- unsigned long lastAvgTime = 0; unsigned long sumElectrode = 0; unsigned int countElectrode = 0; uint16_t avgElectrode = 0; uint16_t plantValue = 0; // --- Last audio signal (for OLED) --- float lastSig = 0.0f; // === Forward declarations === void addGrain(float freq, size_t lifespan, float fmAmount); void AudioCallback(float **in, float **out, size_t size); void setup() { Serial.begin(115200); while(!Serial) {} // OLED init if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)){ while(1); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.println("Initializing..."); display.display(); dht.begin(); // SD card init pinMode(SD_CS_PIN, OUTPUT); digitalWrite(SD_CS_PIN, HIGH); SPI.begin(); if(!sd.begin(SD_CS_PIN, SD_SCK_MHZ(4))){ display.println("SD init failed!"); display.display(); while(1); } // Open WAV wavFile = sd.open(WAV_FILE_NAME, FILE_READ); if(!wavFile){ display.println("Cannot open WAV!"); display.display(); while(1); } // Read WAV into external SDRAM (skip 44-byte header) wavSize = wavFile.size() - 44; if(wavSize > MAX_WAV_SAMPLES) wavSize = MAX_WAV_SAMPLES; wavFile.seek(44); wavFile.read((uint8_t*)wavBuffer, wavSize * 2); // 16-bit PCM wavFile.close(); display.println("WAV loaded!"); display.display(); // Daisy audio init DAISY.init(DAISY_SEED, AUDIO_SR_48K); DAISY.begin(AudioCallback); analogReadResolution(16); pinMode(ELECTRODE_PIN, INPUT); display.clearDisplay(); display.println("System ready"); display.display(); } void loop() { plantValue = analogRead(ELECTRODE_PIN); float temp = dht.readTemperature(); float hum = dht.readHumidity(); // 1-second electrode average sumElectrode += plantValue; countElectrode++; if(millis() - lastAvgTime >= 1000){ avgElectrode = sumElectrode / countElectrode; sumElectrode = 0; countElectrode = 0; lastAvgTime = millis(); } // OLED display display.clearDisplay(); display.setCursor(0,0); display.print("Temp: "); display.print(temp,1); display.println(" C"); display.print("Hum: "); display.print(hum,1); display.println(" %"); display.print("Plant:"); display.print(plantValue); display.println(); display.print("Avg: "); display.print(avgElectrode); display.println(); // Active grains int activeGrains = 0; for(size_t g=0; g0) activeGrains++; display.print("Grains: "); display.println(activeGrains); // Last audio level display.print("Sig: "); display.println((int)(lastSig*1000)); display.display(); // Generate new grain occasionally if(millis() - lastGrainTime > map(temp, 10, 40, 100, 2000)){ size_t randomStart = random(0, wavSize - 48000); // 1 second grain addGrain( map(plantValue, 0, 45000, 0, 100), // pitch 48000, // 1 second lifespan 0.05f // FM ); grains[MAX_GRAINS-1].startPos = randomStart; // assign start lastGrainTime = millis(); } delay(10); } void addGrain(float freq, size_t lifespan, float fmAmount){ for(size_t i=0;i0){ float fm = grains[g].fmAmount * sinf(2.0f*PI*grains[g].phase*3.0f); size_t pos = grains[g].startPos + (size_t)((grains[g].phase + fm) * 48000); pos %= wavSize; float samp = (float)wavBuffer[pos] / 32768.0f; sig += samp * grains[g].env; grains[g].phase += grains[g].freq / 48000.0f; if(grains[g].phase>=1.0f) grains[g].phase -=1.0f; if(grains[g].lifespan<100) grains[g].env *= 0.99f; grains[g].lifespan--; activeGrains++; } } if(activeGrains>1) sig/=activeGrains; float gain = 0.5f; // adjust overall volume out[0][i] = sig*gain; out[1][i] = sig*gain; lastSig = sig*gain; } }