Recording audio while sensing capacitive touch -- feasible?

Status
Not open for further replies.

lazordisk

New member
Hello! For my project, I am trying to build a cube that records audio while a user is holding it (and then plays it back later). I'm using the Teensy 4.1 with Rev D audio shield, Adafruit's SPH0645 microphone (https://www.adafruit.com/product/3421), and the Max98306 amplifier breakout board (https://www.adafruit.com/product/987) to boost line out audio from the audio shield to a pair of speakers. Pictures of the current setup:
teensy10.jpg
teensy11.jpg

Currently the microphone records fine, saves to the SD card fine, and plays audio back fine (though there is a little noise and it is a little quiet).

For capacitive sensing I'm using the Capacitive Sensor library to run wires on pins 19 and 16 to create a capacitive sensing circuit, which also works fine on its own. (https://playground.arduino.cc/Main/CapacitiveSensor/)

The problem is that if I try and read a capacitive touch with capacitiveSensor during recording of audio, the audio can't record at the same time. I'm not that great at physical computing, and I can only imagine it's a problem of the microcontroller not being able to do both things at once, but I thought maybe someone else would have an idea of how to skirt around the problem. I tried only reading capacitive touch every two seconds, but even that resulted in an audible break in the recording. This is a problem because I need to know when the user has let go of the cube.

Current code is below -- some of the experimentation I was doing with averaging to smooth the signal is commented out. Thank you in advance for any advice!

Code:
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

#include <Metro.h> // Include the Metro library

// GUItool: begin automatically generated code
AudioInputI2S2           i2s2_1;         //xy=264.00000381469727,191.00000286102295
AudioAnalyzePeak         peak1;          //xy=766,134
AudioRecordQueue         queue1;         //xy=780,200
AudioFilterStateVariable filter1;        //xy=422.00000762939453,191.00000190734863
AudioAmplifier           amp1;           //xy=545.0000076293945,192.00000286102295
AudioAnalyzeFFT1024      fft1024_1;      //xy=829.0000114440918,297.00000190734863

AudioPlaySdRaw           playSdRaw1;     //xy=347.00000762939453,290.00000381469727
AudioOutputI2S           i2s1;           //xy=594,291

AudioConnection          patchCord1(i2s2_1, 0, filter1, 0);
AudioConnection          patchCord2(playSdRaw1, 0, i2s1, 0);
AudioConnection          patchCord3(playSdRaw1, 0, i2s1, 1);
AudioConnection          patchCord4(filter1, 2, amp1, 0);
AudioConnection          patchCord5(amp1, fft1024_1);
AudioConnection          patchCord6(amp1, queue1);
AudioConnection          patchCord7(amp1, peak1);
AudioControlSGTL5000     sgtl5000_1;     //xy=510.00000762939453,403.0000057220459
// GUItool: end automatically generated code

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing, 3=stopped

long int currentFile = 0;

// The file where data is recorded
File frec;

//CAPSENSE STUFF

#include <CapacitiveSensor.h>

CapacitiveSensor   cs_19_16 = CapacitiveSensor(19,16); // 10M resistor between pins 4 & 2, pin 2 is sensor pin, add a wire and or foil if desired
//currently using 1M resistor

//int capTotalOld;
//int capTotal;

Metro capSenseMetro = Metro(2000);  // Instantiate an instance

void setup() {

  cs_19_16.set_CS_AutocaL_Millis(0xFFFFFFFF);     // turn off autocalibrate on channel 1 - just as an example
  
  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  //sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.7);
  sgtl5000_1.lineOutLevel(15);

  //for microphone
  filter1.frequency(30); // filter out DC & extremely low frequencies
  amp1.gain(8.5);        // amplify sign to useful range

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}

void loop() {

  //CAPSENSE AVERAGING
  //---------------------------------
  long start = millis();

  //average = 0;

  //check if 2 seconds have passed
  if (capSenseMetro.check() == 1) {
    long total1 =  cs_19_16.capacitiveSensor(30);
  }

  /*//long total1 =  cs_19_16.capacitiveSensor(30);
  for (int i = 0; i < 30; i++) {
      long total1 =  cs_19_16.capacitiveSensor(30);
      average += total1;
   }

   average /= 30;*/

   /*if((average - oldAverage) > 500 && touchState == 0 && oldAverage != 0) {
      Serial.println("touch started");
      storedValue = oldAverage;
      touchState = 1;
      //Serial.println(storedValue);
    }

    if(touchState == 1 && (average < storedValue)) {
      touchState = 0;
      Serial.println("stopped touching");
    }*/
   //--------------------------------
    
  //RECORDING STUFF
  if (millis() > 7000 && mode == 1) {
    stopRecording();
    delay(1000);
    startPlaying();
  } else if (mode == 1) {
    continueRecording();
  } 

  if (millis() > 2000 && mode == 0) {
    startRecording();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 3) {
    continuePlaying();
  }

  //oldAverage = average;
}


void startRecording() {
  Serial.println("startRecording");
  if (SD.exists("RECORD2.RAW")) {
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
    SD.remove("RECORD2.RAW");
  }
  frec = SD.open("RECORD2.RAW", FILE_WRITE);
  if (frec) {
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  //Serial.println("Recording....");
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    //elapsedMicros usec = 0;
    frec.write(buffer, 512);
    // Uncomment these lines to see how long SD writes
    // are taking.  A pair of audio blocks arrives every
    // 5802 microseconds, so hopefully most of the writes
    // take well under 5802 us.  Some will take more, as
    // the SD library also must write to the FAT tables
    // and the SD card controller manages media erase and
    // wear leveling.  The queue1 object can buffer
    // approximately 301700 us of audio, to allow time
    // for occasional high SD card latency, as long as
    // the average write time is under 5802 us.
    //Serial.print("SD write, us=");
    //Serial.println(usec);
  }
  if (fft1024_1.available()) {
    // each time new FFT data is available
    // print 20 bins to the Arduino Serial Monitor
    Serial.print("FFT: ");
    for (int i = 0; i < 20; i++) {
      float n = fft1024_1.read(i);
      if (n >= 0.001) {
        Serial.print(n, 3);
        Serial.print(" ");
      } else {
        Serial.print("  --  "); // don't print "0.000"
      }
    }
    Serial.println();
  }
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
  mode = 3;
}


void startPlaying() {
  Serial.println("startPlaying");
  playSdRaw1.play("RECORD2.RAW");
  mode = 2;
}

void continuePlaying() {
  if (!playSdRaw1.isPlaying()) {
    playSdRaw1.stop();
    mode = 4;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playSdRaw1.stop();
  mode = 3;
}
 
Teensy 4.1 should be fast enough if your code is using interrupts but I guess the Arduino library is not fast enough, but did you read this? Looks like that lib isn't required when using a Teensy with version number more than 2? Here's a guide for Teensy 3.6.

One simple way to get around this is to use dedicated touch hardware. I've used the MPR121 for several projects and it's very easy to use and offers an IRQ pin that can be used with interrupts to read the device only when a change has occurred. Adafruit has a nice breakout for this https://www.adafruit.com/product/1982
 
Status
Not open for further replies.
Back
Top