main.cpp
#include <Arduino.h>
#include <ESP_I2S.h>
 
// Sparrow mic pins (ICS-43434, I2S):
// MIC_SCK  -> GPIO18 (bit clock / bclk / sck)
// MIC_WS   -> GPIO20 (word select / ws / LRCLK)
// MIC_SD   -> GPIO19 (data from mic -> esp din)
//
// From Sparrow README pin table. (MIC_WS=GPIO20, MIC_SCK=GPIO18, MIC_SD=GPIO19)
//
// We'll set dout = -1 because Sparrow is only listening, not transmitting audio out.
 
static I2SClass I2S;
 
// recording config
static const uint32_t SAMPLE_RATE_HZ = 16000;
static const size_t   RECORD_SECONDS = 1;   // 1-second clips
// We'll get 16k samples/sec * 2 bytes/sample = 32000 bytes + WAV header per clip.
 
void setupI2SMic() {
  // Attach pins: setPins(bclk, ws, dout, din=-1, mclk=-1)
  I2S.setPins(
    18,     // bclk / SCK
    20,     // ws / LRCLK
    -1,     // dout not used (speaker out)
    19,     // din (mic data line)
    -1      // mclk not used
  );
 
  // Start I2S in standard mode @16kHz.
  // begin(mode, rate, bits_per_sample, slot_mode)
  // We ask for 32-bit slots stereo here, then we'll configure RX to give us 16-bit mono.
  // Why: ICS-43434 outputs a single channel in I2S format where the left slot contains mic data. :contentReference[oaicite:5]{index=5}
  // We'll downconvert with configureRX(..., I2S_RX_TRANSFORM_32_TO_16)
  bool ok = I2S.begin(
    I2S_MODE_STD,
    SAMPLE_RATE_HZ,
    I2S_DATA_BIT_WIDTH_32BIT,
    I2S_SLOT_MODE_STEREO
  );
  if(!ok){
    Serial.println("ERR I2S.begin failed");
  }
 
  // configureRX(rate, bits_cfg, ch, transform)
  // We'll request 32-bit/stereo from the bus, then have the driver transform samples
  // to 16-bit mono for us.
  ok = I2S.configureRX(
    SAMPLE_RATE_HZ,
    I2S_DATA_BIT_WIDTH_32BIT,
    I2S_SLOT_MODE_STEREO,
    I2S_RX_TRANSFORM_32_TO_16
  );
  if(!ok){
    Serial.println("ERR I2S.configureRX failed");
  }
}
 
// Record N seconds of audio into RAM as a WAV buffer using recordWAV().
// This gives us a pointer + size. We then push that over Serial.
void recordAndSend(const char *label) {
  size_t wav_size = 0;
  uint8_t *wav_buf = I2S.recordWAV(RECORD_SECONDS, &wav_size);
  if(!wav_buf || wav_size == 0){
    Serial.println("ERR recordWAV failed");
    return;
  }
 
  // Send header line so host knows what's coming.
  Serial.printf("CHUNK %s %u\n", label, (unsigned)wav_size);
 
  // Send raw WAV bytes (binary).
  Serial.write(wav_buf, wav_size);
 
  // newline then END so host knows it's done.
  Serial.print("\nEND\n");
 
  // free buffer from recordWAV
  free(wav_buf);
}
 
void setup() {
  Serial.begin(115200);
  delay(2000); // allow USB CDC to settle, ESP32-C6 can reset on open
 
  setupI2SMic();
 
  Serial.println("# Sparrow voice dataset recorder ready.");
  Serial.println("# Send 'hello' or 'other' on its own line.");
}
 
void loop() {
  // check if PC sent us a command
  static String cmd;
  while(Serial.available()){
    char c = Serial.read();
    if(c == '\n' || c == '\r'){
      if(cmd.length() > 0){
        if(cmd == "hello"){
          recordAndSend("hello");
        } else {
          // treat anything not "hello" as negative/background class
          recordAndSend("other");
        }
        cmd = "";
      }
    } else {
      cmd += c;
    }
  }
 
  delay(5);
}