I am trying to output audio from an RN-52 to a Teensy 4.0 with an audio shield (though I’m not sure if the shield is necessary). I am running FFT on the audio and I have gotten it working (not reliably) with one speaker output from the RN-52 to the audio shield’s line in, but this only captures one channel of audio. If I connect both, the RN-52 quickly shuts down for some reason.
So I thought I2S might be the way to go. I have tried everything to think of to get the I2S connection working but I just can’t figure it out and there is no other clear guide online for how to do so, including the Teensy audio design tool. I connected the wires on the Teensy as instructed and connected them to the PCM pins in every combination but I can’t get any FFT reading.
The code I’m using that has produced FFT before but isn’t now for some reason is:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioInputI2S i2s1; //xy=390,442
AudioAnalyzeFFT256 fft256_1; //xy=762,463
AudioConnection patchCord1(i2s1, 0, fft256_1, 0);
AudioConnection patchCord2(i2s1, 1, fft256_1, 0);
AudioControlSGTL5000 sgtl5000_1; //xy=520,292
// GUItool: end automatically generated code
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
void setup() {
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(12);
// Enable the audio shield and set the output volume.
sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.5);
// Configure the window algorithm to use
fft256_1.windowFunction(AudioWindowHanning1024);
//fft256_1.windowFunction(NULL);
// Create a synthetic sine wave, for testing
// To use this, edit the connections above
// sinewave.amplitude(0.8);
// sinewave.frequency(1034.007);
}
void loop() {
float n;
int i;
if (fft256_1.available()) {
// each time new FFT data is available
// print it all to the Arduino Serial Monitor
Serial.print("FFT: ");
for (i=0; i<40; i++) {
n = fft256_1.read(i);
if (n >= 0.01) {
Serial.print(n);
Serial.print(" ");
} else {
Serial.print(" - "); // don't print "0.00"
}
}
Serial.println();
}
}
Ultimately I just need to do FFT on Bluetooth audio. I asked about this in the other thread, but the ESP32 route seems just as difficult from my amateur perspective. That said, if you think that is easier, I’m willing to eat the cost of these boards. I just need something that works.
Thank you in advance for your guidance!
Below is a checklist that has solved this exact “I2S-in to Teensy works once, then never again” problem for dozens of people.
Work through the items in order; stop as soon as the FFT starts printing data again.
(Everything is 3.3 V; never connect 5 V directly to the RN-52 or Teensy.)
Work through the items in order; stop as soon as the FFT starts printing data again.
(Everything is 3.3 V; never connect 5 V directly to the RN-52 or Teensy.)
- Get the RN-52 to master the I²S bus
The RN-52 is happiest when it drives BCLK and LRCLK.
Issue the following commands over the RN-52 UART (115 200 8 N 1):
Issue the following commands over the RN-52 UART (115 200 8 N 1):
plain
复制
S|, // restore factory defaults
SP,0 // I²S master mode (RN-52 drives BCLK/LRCLK)
R,1 // rebootAfter reboot the pins should show:
plain
复制
PCM_CLK (BCLK) 2.82 MHz @ 44.1 kHz
PCM_SYNC (LRCLK) 44.1 kHz
PCM_OUT (DATA) valid I²S data- Wire only the three I²S signals
Teensy 4.0 pin → RN-52 pin
plain
复制
7 BCLK ← PCM_CLK
8 LRCLK ← PCM_SYNC
9 DIN ← PCM_OUT No MCLK is required; the SGTL5000 on the shield is clocked by Teensy itself.
Leave PCM_IN (RN-52’s I²S input) unconnected; tying it to GND or 3.3 V can shut the module down when no data are present.
Leave PCM_IN (RN-52’s I²S input) unconnected; tying it to GND or 3.3 V can shut the module down when no data are present.
- Tell the Audio library to look at I²S input, not the shield
Delete every reference to the SGTL5000 in the sketch; the shield is not
involved when you take digital audio straight into Teensy.
cpp
复制
#include <Audio.h>
AudioInputI2S i2s1; // Teensy I²S slave input
AudioAnalyzeFFT256 fftL;
AudioAnalyzeFFT256 fftR;
AudioConnection patchCord1(i2s1, 0, fftL, 0);
AudioConnection patchCord2(i2s1, 1, fftR, 0);
void setup() {
AudioMemory(12);
// NOTHING for the SGTL5000 – we are not using it
fftL.windowFunction(AudioWindowHanning256);
fftR.windowFunction(AudioWindowHanning256);
Serial.begin(115200);
}
void loop() {
if (fftL.available()) {
Serial.print("L: ");
for (int i = 0; i < 40; i++) {
float v = fftL.read(i);
Serial.print(v > 0.01 ? v : 0);
Serial.print(' ');
}
Serial.println();
}
}- Verify clocks with a scope or a cheap logic analyser
You must see
- BCLK 64 × 44.1 kHz ≈ 2.8 MHz
- LRCLK 44.1 kHz
- Data changing on every BCLK falling edge.
If the clocks disappear after a few seconds the RN-52 is still in slave mode
(SP,1) – go back to step 1.
- If you still get silence
a) Add 100 Ω series resistors on each I²S line (close to the Teensy side).
The RN-52 pads are 3.3 V CMOS and can ring badly on a breadboard.
b) Make sure the RN-52 is actually streaming audio – connect a headphone to its speaker outputs; you should hear the Bluetooth source.
c) Pull up RN-52 GPIO9 (Pin 16 on the module) to 3.3 V; this forces the module to keep the DAC and I²S enabled even when no speaker load is detected.
The RN-52 pads are 3.3 V CMOS and can ring badly on a breadboard.
b) Make sure the RN-52 is actually streaming audio – connect a headphone to its speaker outputs; you should hear the Bluetooth source.
c) Pull up RN-52 GPIO9 (Pin 16 on the module) to 3.3 V; this forces the module to keep the DAC and I²S enabled even when no speaker load is detected.
- Optional: combine L+R into a single FFT
If you only care about “is there bass / mid / treble energy” you can sum the
two channels in software instead of wiring them together externally:
cpp
复制
AudioMixer4 mixer;
AudioAnalyzeFFT256 fftMono;
AudioConnection patchCord1(i2s1, 0, mixer, 0);
AudioConnection patchCord2(i2s1, 1, mixer, 1);
AudioConnection patchCord3(mixer, fftMono);
// in setup: mixer.gain(0,0.5); mixer.gain(1,0.5);- If nothing ever works – fallback that does work
Use the RN-52’s speaker outputs, but take only one channel (say SPK-) and
AC-couple it to the Teensy ADC through a 10 µF cap and a 1:2 resistor divider
(22 k / 10 k). Run the ADC at 44.1 kHz with the Audio library’s
AudioInputAnalog object. Quality is lower, but you get a reliable FFT on
Bluetooth audio with zero I²S hassle.Start with step 1 – set the RN-52 to master, wire the three pins listed in
step 2, flash the minimal sketch from step 3, and open the serial monitor at
115 200 baud. You should see FFT numbers scrolling immediately when music
plays on the phone.