Teensy 4.1 and Audio Shield revision D2 - Which pins should I connect

I'm super noob in electronics and I'm trying to make a simple audio project for fun to learn more.

With a button on the breadboard, I want to start a recording audio when I press the button.
These recordings will be stored on the Teensy 4.1 SDCard.
The audio will be out via 3.5mm jack and audio in will be via a microphone which has just two wires.

My main issue is, how do I know which pins from Audio Board D2 to connect to Teensy 4.1 and where.

Can anyone explain-me what is the rational on figuring out how to discover which pins should I connect from one point to another ?
Should it be ground to ground, 3.3v to 3.3v and then I pick pins from teensy and via code I control the Audio Shield ?

I haven't connected anything between the audio shield and the Teensy yet as I don't want to damage anything.

Thank you
 
Can anyone explain-me what is the rational on figuring out how to discover which pins should I connect from one point to another ?
Should it be ground to ground, 3.3v to 3.3v and then I pick pins from teensy and via code I control the Audio Shield ?

@calbertoferreira:

Take a look at <this> webpage, which covers the signals required to connect the Audio Adapter Board to the Teensy 4.1. Welcome to the Teensy world . . . you are in for a ton of fun !! Feel free to ask any other questions that may come up, as there are folks on this forum with lots of knowledge and experience with the Teensy, and they are very willing to provide help.

Hope that helps . . .

Mark J Culross
KD5RXT

P.S. the Audio Adapter Boards rev D & D2 are laid out so that they can be easily stacked with the Teensy 4.x. I prefer to have my Audio Adapter Board on bottom and the Teensy 4.x on top (for easy access to the programming button), but they work together either way. Just make sure to use soldered connections between the two boards and try to keep the connections as short as possible. Good luck & have fun !! MJC
 
Thanks for the reply @kd5rxt-mark

After looking into Teensy and Audio Shield D2 pins, I made a spreadsheet and it's simple, it's basically one to one match (eg: MCLK is pin 23 on both boards, SCL is pin 19, etc).
I also believe I probably only need the following pins as the goal is just to play audio to 3.5 mm jack and record from a microphone, and the SD card will be on Teensy:
Pins.png

Can you please confirm ?

For now, I soldered pins into the Audio Shield D2 and two wires so later I can plug the microphone like this:
IMG_4566 (Medium).jpg

I tried to stack both boards one on top of the other with the matching pins and I can't hear sound via 3.5mm audio jack.
I used the following code from the Audio guest book project (https://github.com/playfultechnology/audio-guestbook) and changed it so it started playing the wave file in the SD card immediately instead of waiting for a button press.

Code:
//#include <_Teensy.h>

/**
 * Audio Guestbook, Copyright (c) 2022 Playful Technology
 * 
 * Tested using a Teensy 4.0 with Teensy Audio Shield, although should work 
 * with minor modifications on other similar hardware
 * 
 * When handset is lifted, a pre-recorded greeting message is played, followed by a tone.
 * Then, recording starts, and continues until the handset is replaced.
 * Playback button allows all messages currently saved on SD card through earpiece 
 * 
 * Files are saved on SD card as 44.1kHz, 16-bit, mono signed integer RAW audio format 
 * --> changed this to WAV recording, DD4WH 2022_07_31
 * --> added MTP support, which enables copying WAV files from the SD card via the USB connection, DD4WH 2022_08_01
 * 
 * 
 * Frank DD4WH, August 1st 2022 
 * for a DBP 611 telephone (closed contact when handheld is lifted) & with recording to WAV file
 * contact for switch button 0 is closed when handheld is lifted
 * 
 * GNU GPL v3.0 license
 * 
 */
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
#include <MTP_Teensy.h>
#include "play_sd_wav.h"  // local copy with fixes

// DEFINES
// Define pins used by Teensy Audio Shield
#define SDCARD_CS_PIN BUILTIN_SDCARD //10
// #define SDCARD_MOSI_PIN 7
// #define SDCARD_SCK_PIN 14

// And those used for inputs
#define HOOK_PIN 0
#define PLAYBACK_BUTTON_PIN 1

#define noINSTRUMENT_SD_WRITE

int chipSelect = BUILTIN_SDCARD;

// GLOBALS
// Audio initialisation code can be generated using the GUI interface at https://www.pjrc.com/teensy/gui/
// Inputs
AudioSynthWaveform waveform1;                        // To create the "beep" sfx
AudioInputI2S i2s2;                                  // I2S input from microphone on audio shield
AudioPlaySdWavX playWav1;                            // Play 44.1kHz 16-bit PCM greeting WAV file
AudioRecordQueue queue1;                             // Creating an audio buffer in memory before saving to SD
AudioMixer4 mixer;                                   // Allows merging several inputs to same output
AudioOutputI2S i2s1;                                 // I2S interface to Speaker/Line Out on Audio shield
AudioConnection patchCord1(waveform1, 0, mixer, 0);  // wave to mixer
AudioConnection patchCord3(playWav1, 0, mixer, 1);   // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0);       // mixer output to speaker (L)
AudioConnection patchCord6(mixer, 0, i2s1, 1);       // mixer output to speaker (R)
AudioConnection patchCord5(i2s2, 0, queue1, 0);      // mic input to queue (L)
AudioControlSGTL5000 sgtl5000_1;

// Filename to save audio recording on SD card
char filename[15];
// The file object itself
File frec;

// Use long 40ms debounce time on both switches
Bounce buttonRecord = Bounce(HOOK_PIN, 1);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 40);

// Keep track of current state of the device
enum Mode { Initialising,
            Ready,
            Prompting,
            Recording,
            Playing };

Mode mode = Mode::Initialising;

float beep_volume = 0.04f;  // not too loud :-)

uint32_t MTPcheckInterval;  // default value of device check interval [ms]

// variables for writing to WAV file
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned int AudioFormat = 1;
unsigned int numChannels = 1;
unsigned long sampleRate = 44100;
unsigned int bitsPerSample = 16;
unsigned long byteRate = sampleRate * numChannels * (bitsPerSample / 8);  // samplerate x channels x (bitspersample / 8)
unsigned int blockAlign = numChannels * bitsPerSample / 8;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;

void setup() {

  Serial.begin(9600);
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }
  Serial.println("Serial set up correctly");
  Serial.printf("Audio block set to %d samples\n", AUDIO_BLOCK_SAMPLES);
  print_mode();
  // Configure the input pins
  pinMode(HOOK_PIN, INPUT_PULLUP);
  pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);

  // 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();
  // Define which input on the audio shield to use (AUDIO_INPUT_LINEIN / AUDIO_INPUT_MIC)
  sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
  sgtl5000_1.adcHighPassFilterDisable(); //
  sgtl5000_1.volume(15);

  mixer.gain(0, 1.0f);
  mixer.gain(1, 1.0f);

  // Play a beep to indicate system is online
  waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
  wait(1000);
  waveform1.amplitude(0);
  delay(1000);

  // Initialize the SD card
  //UNCOMMENT THESE TWO LINES FOR TEENSY AUDIO BOARD:
  // SPI.setMOSI(SDCARD_MOSI_PIN);
  // SPI.setSCK(SDCARD_SCK_PIN);

  if (!(SD.begin(BUILTIN_SDCARD))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  } else Serial.println("SD card correctly initialized");


  // mandatory to begin the MTP session.
  MTP.begin();

  // Add SD Card
  //    MTP.addFilesystem(SD, "SD Card");
  MTP.addFilesystem(SD, "Audio guestbook");  // choose a nice name for the SD card volume to appear in your file explorer
  Serial.println("Added SD card via MTP");
  MTPcheckInterval = MTP.storage()->get_DeltaDeviceCheckTimeMS();

  // Value in dB
  sgtl5000_1.micGain(15);
  // sgtl5000_1.micGain(5);  // much lower gain is required for the AOM5024 electret capsule

  // Synchronise the Time object used in the program code with the RTC time provider.
  // See https://github.com/PaulStoffregen/Time
  setSyncProvider(getTeensy3Time);

  // Define a callback that will assign the correct datetime for any file system operations
  // (i.e. saving a new audio recording onto the SD card)
  FsDateTime::setCallback(dateTime);

  mode = Mode::Prompting;
  print_mode();
}

void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonPlay.update();

  switch (mode) {
    case Mode::Ready:
      // Falling edge occurs when the handset is lifted --> 611 telephone
      if (buttonRecord.fallingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting;
        print_mode();
      } else if (buttonPlay.fallingEdge()) {
        //playAllRecordings();
        playLastRecording();
      }
      break;

    case Mode::Prompting:
      // Wait a second for users to put the handset to their ear
      wait(1000);
      // Play the greeting inviting them to record their message
      playWav1.play("all_greetings.wav");
      // Wait until the  message has finished playing
      //      while (playWav1.isPlaying()) {
      while (!playWav1.isStopped()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        buttonPlay.update();
        // Handset is replaced
        if (buttonRecord.risingEdge()) {
          playWav1.stop();
          mode = Mode::Ready;
          print_mode();
          return;
        }
        if (buttonPlay.fallingEdge()) {
          playWav1.stop();
          //playAllRecordings();
          playLastRecording();
          return;
        }
      }
      // Debug message
      Serial.println("Starting Recording");
      // Play the tone sound effect
      waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
      wait(1250);
      waveform1.amplitude(0);
      // Start the recording function
      //startRecording();
      break;

    case Mode::Recording:
      // Handset is replaced
      if (buttonRecord.risingEdge()) {
        // Debug log
        Serial.println("Stopping Recording");
        // Stop recording
        stopRecording();
        // Play audio tone to confirm recording has ended
        end_Beep();
      } else {
        continueRecording();
      }
      break;

    case Mode::Playing:  // to make compiler happy
      break;

    case Mode::Initialising:  // to make compiler happy
      break;
  }

  MTP.loop();  // This is mandatory to be placed in the loop code.
}

void setMTPdeviceChecks(bool nable) {
  if (nable) {
    MTP.storage()->set_DeltaDeviceCheckTimeMS(MTPcheckInterval);
    Serial.print("En");
  } else {
    MTP.storage()->set_DeltaDeviceCheckTimeMS((uint32_t)-1);
    Serial.print("Dis");
  }
  Serial.println("abled MTP storage device checks");
}


#if defined(INSTRUMENT_SD_WRITE)
static uint32_t worstSDwrite, printNext;
#endif  // defined(INSTRUMENT_SD_WRITE)

void startRecording() {
  setMTPdeviceChecks(false);  // disable MTP device checks while recording
#if defined(INSTRUMENT_SD_WRITE)
  worstSDwrite = 0;
  printNext = 0;
#endif  // defined(INSTRUMENT_SD_WRITE)
  // Find the first available file number
  //  for (uint8_t i=0; i<9999; i++) { // BUGFIX uint8_t overflows if it reaches 255
  for (uint16_t i = 0; i < 9999; i++) {
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // Create if does not exist, do not open existing, write, sync after write
    if (!SD.exists(filename)) {
      break;
    }
  }
  frec = SD.open(filename, FILE_WRITE);
  Serial.println("Opened file !");
  if (frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording;
    print_mode();
    recByteSaved = 0L;
  } else {
    Serial.println("Couldn't open file to record!");
  }
}

void continueRecording() {
#if defined(INSTRUMENT_SD_WRITE)
  uint32_t started = micros();
#endif  // defined(INSTRUMENT_SD_WRITE)
#define NBLOX 16
  // Check if there is data in the queue
  if (queue1.available() >= NBLOX) {
    byte buffer[NBLOX * AUDIO_BLOCK_SAMPLES * sizeof(int16_t)];
    // 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.
    for (int i = 0; i < NBLOX; i++) {
      memcpy(buffer + i * AUDIO_BLOCK_SAMPLES * sizeof(int16_t), queue1.readBuffer(), AUDIO_BLOCK_SAMPLES * sizeof(int16_t));
      queue1.freeBuffer();
    }
    // Write all 512 bytes to the SD card
    frec.write(buffer, sizeof buffer);
    recByteSaved += sizeof buffer;
  }

#if defined(INSTRUMENT_SD_WRITE)
  started = micros() - started;
  if (started > worstSDwrite)
    worstSDwrite = started;

  if (millis() >= printNext) {
    Serial.printf("Worst write took %luus\n", worstSDwrite);
    worstSDwrite = 0;
    printNext = millis() + 250;
  }
#endif  // defined(INSTRUMENT_SD_WRITE)
}

void stopRecording() {
  // Stop adding any new data to the queue
  queue1.end();
  // Flush all existing remaining data from the queue
  while (queue1.available() > 0) {
    // Save to open file
    frec.write((byte*)queue1.readBuffer(), AUDIO_BLOCK_SAMPLES * sizeof(int16_t));
    queue1.freeBuffer();
    recByteSaved += AUDIO_BLOCK_SAMPLES * sizeof(int16_t);
  }
  writeOutHeader();
  // Close the file
  frec.close();
  Serial.println("Closed file");
  mode = Mode::Ready;
  print_mode();
  setMTPdeviceChecks(true);  // enable MTP device checks, recording is finished
}


void playAllRecordings() {
  // Recording files are saved in the root directory
  File dir = SD.open("/");

  while (true) {
    File entry = dir.openNextFile();
    if (strstr(entry.name(), "greeting")) {
      entry = dir.openNextFile();
    }
    if (!entry) {
      // no more files
      entry.close();
      end_Beep();
      break;
    }
    //int8_t len = strlen(entry.name()) - 4;
    //    if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
    //    if (strstr(strlwr(entry.name() + (len - 4)), ".wav")) {
    // the lines above throw a warning, so I replace them with this (which is also easier to read):
    if (strstr(entry.name(), ".wav") || strstr(entry.name(), ".WAV")) {
      Serial.print("Now playing ");
      Serial.println(entry.name());
      // Play a short beep before each message
      waveform1.amplitude(beep_volume);
      wait(750);
      waveform1.amplitude(0);
      // Play the file
      playWav1.play(entry.name());
      mode = Mode::Playing;
      print_mode();
    }
    entry.close();

    //    while (playWav1.isPlaying()) { // strangely enough, this works for playRaw, but it does not work properly for playWav
    while (!playWav1.isStopped()) {  // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
      //      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
      if (buttonPlay.fallingEdge() || buttonRecord.risingEdge()) {
        playWav1.stop();
        mode = Mode::Ready;
        print_mode();
        return;
      }
    }
  }
  // All files have been played
  mode = Mode::Ready;
  print_mode();
}

void playLastRecording() {
  // Find the first available file number
  uint16_t idx = 0;
  for (uint16_t i = 0; i < 9999; i++) {
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // check, if file with index i exists
    if (!SD.exists(filename)) {
      idx = i - 1;
      break;
    }
  }
  // now play file with index idx == last recorded file
  snprintf(filename, 11, " %05d.wav", idx);
  Serial.println(filename);
  playWav1.play(filename);
  mode = Mode::Playing;
  print_mode();
  while (!playWav1.isStopped()) {  // this works for playWav
    buttonPlay.update();
    buttonRecord.update();
    // Button is pressed again
    //      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
    if (buttonPlay.fallingEdge() || buttonRecord.risingEdge()) {
      playWav1.stop();
      mode = Mode::Ready;
      print_mode();
      return;
    }
  }
  // file has been played
  mode = Mode::Ready;
  print_mode();
  end_Beep();
}


// Retrieve the current time from Teensy built-in RTC
time_t getTeensy3Time() {
  return Teensy3Clock.get();
}

// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

  // Return date using FS_DATE macro to format fields.
  *date = FS_DATE(year(), month(), day());

  // Return time using FS_TIME macro to format fields.
  *time = FS_TIME(hour(), minute(), second());

  // Return low time bits in units of 10 ms.
  *ms10 = second() & 1 ? 100 : 0;
}

// Non-blocking delay, which pauses execution of main program logic,
// but while still listening for input
void wait(unsigned int milliseconds) {
  elapsedMillis msec = 0;

  while (msec <= milliseconds) {
    buttonRecord.update();
    buttonPlay.update();
    if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    if (buttonPlay.risingEdge()) Serial.println("Button (pin 1) Release");
  }
}


void writeOutHeader() {  // update WAV header with final filesize/datasize

  //  NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
  //  Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
  Subchunk2Size = recByteSaved - 42;  // because we didn't make space for the header to start with! Lose 21 samples...
  ChunkSize = Subchunk2Size + 34;     // was 36;
  frec.seek(0);
  frec.write("RIFF");
  byte1 = ChunkSize & 0xff;
  byte2 = (ChunkSize >> 8) & 0xff;
  byte3 = (ChunkSize >> 16) & 0xff;
  byte4 = (ChunkSize >> 24) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  frec.write(byte3);
  frec.write(byte4);
  frec.write("WAVE");
  frec.write("fmt ");
  byte1 = Subchunk1Size & 0xff;
  byte2 = (Subchunk1Size >> 8) & 0xff;
  byte3 = (Subchunk1Size >> 16) & 0xff;
  byte4 = (Subchunk1Size >> 24) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  frec.write(byte3);
  frec.write(byte4);
  byte1 = AudioFormat & 0xff;
  byte2 = (AudioFormat >> 8) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  frec.write(byte3);
  frec.write(byte4);
  byte1 = byteRate & 0xff;
  byte2 = (byteRate >> 8) & 0xff;
  byte3 = (byteRate >> 16) & 0xff;
  byte4 = (byteRate >> 24) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  frec.write(byte3);
  frec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  frec.write("data");
  byte1 = Subchunk2Size & 0xff;
  byte2 = (Subchunk2Size >> 8) & 0xff;
  byte3 = (Subchunk2Size >> 16) & 0xff;
  byte4 = (Subchunk2Size >> 24) & 0xff;
  frec.write(byte1);
  frec.write(byte2);
  frec.write(byte3);
  frec.write(byte4);
  frec.close();
  Serial.println("header written");
  Serial.print("Subchunk2: ");
  Serial.println(Subchunk2Size);
}

void end_Beep(void) {
  waveform1.frequency(523.25);
  waveform1.amplitude(beep_volume);
  wait(250);
  waveform1.amplitude(0);
  wait(250);
  waveform1.amplitude(beep_volume);
  wait(250);
  waveform1.amplitude(0);
  wait(250);
  waveform1.amplitude(beep_volume);
  wait(250);
  waveform1.amplitude(0);
  wait(250);
  waveform1.amplitude(beep_volume);
  wait(250);
  waveform1.amplitude(0);
}

void print_mode(void) {  // only for debugging
  Serial.print("Mode switched to: ");
  // Initialising, Ready, Prompting, Recording, Playing
  if (mode == Mode::Ready) Serial.println(" Ready");
  else if (mode == Mode::Prompting) Serial.println(" Prompting");
  else if (mode == Mode::Recording) Serial.println(" Recording");
  else if (mode == Mode::Playing) Serial.println(" Playing");
  else if (mode == Mode::Initialising) Serial.println(" Initialising");
  else Serial.println(" Undefined");
}

Any tips on how to troubleshoot the lack of audio ?

Thank you
 
Last edited:
calbertoferreira:

Yes, I confirm that you have the correct pins. In addition to these pins, as you said in your earlier post, you will also need to connect GROUND & the 3.3VDC pins. The T4.x has multiple GROUND pins, but the Audio Adapter Board uses a very specific GROUND pin. One way to be 100% sure that the needed GROUND pin is connected would be to connect every pin between the T4.x & the Audio Adapter Board (connecting the unused pins will not have any adverse effects). Again, note that these connections between the two will need to be soldered.

Instead of the audio guestbook application (which lots of others have had significant trouble with), I would suggest that you start by using one of the audio adapter example files available in the Arduino IDE for testing. Once you're confident that you have the hardware working, then you can progress to the other application.

Hope that helps . . . good luck & have fun !!

Mark J Culross
KD5RXT
 
Last edited:
Just wanted to thank you for all the help kd5rxt-mark.
Using the audio adapter example files as you suggested help me set everything working.
After that I was able to successful complete the audio book project!
Thanks you!
 
Back
Top