Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: Teensy 4.0 Bluetooth A2DP Wireless Audio Stream to I2S

  1. #1
    Senior Member
    Join Date
    Jun 2018

    Teensy 4.0 Bluetooth A2DP Wireless Audio Stream to I2S


    My project requires a Bluetooth A2DP Sink that is the fed to the Teensy 4 via I2S. I'm leaning towards an ESP32 chip connected to the Teensy for both control over its Bluetooth pairing functions, as well as input of it's bluetooth audio.

    - There is sample code in the ESP32 API.
    - There is sample code in the ESP32 Mp3 Decoder which also shows some hardware pin-outs for connecting the ESP32 to I2S.

    Are there any projects that are doing this that I should look at? Since the new Teensy 4 has some additional I2S data lines, I'm looking to add Bluetooth Audio to my project and send the data to the Teensy via I2S.


  2. #2
    Senior Member
    Join Date
    Jun 2018

    Over a year later and I'm still at it! The ESP32 implementation works ok, but I didn't like the workflow. I searched on a way to build the project with Arduino IDE instead, and found that a user pschatzmann on github made this ESP32-A2DP project, and it compiles fine!

    OK, so fire up a sketch and hook up I2S to my project like this.

    Click image for larger version. 

Name:	esp32_a2dp_test2.jpg 
Views:	11 
Size:	157.2 KB 
ID:	22223

    As posted on the github project page, and similarly to the ESP32 I2S library, you can define the I2S stream. Here is my code:

      Streaming Music from Bluetooth Phil Schatzmann
      Copyright (C) 2020 Phil Schatzmann
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      GNU General Public License for more details.
      You should have received a copy of the GNU General Public License
      along with this program.  If not, see <>.
    //default I2S pins: – bck_io_num = 26, – ws_io_num = 25, – data_out_num = 22,
    #include "BluetoothA2DPSink.h"
    BluetoothA2DPSink a2dp_sink;
    void setup() {
    static const i2s_config_t i2s_config = {
    .mode = (i2s_mode_t) (I2S_MODE_SLAVE | I2S_MODE_TX ),
    .sample_rate = 44100, // corrected by info from bluetooth
    .bits_per_sample = (i2s_bits_per_sample_t) 32, /* the DAC module will only take the 8bits from MSB */
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, 
    .communication_format = (i2s_comm_format_t) (0X01), //I2S_COMM_FORMAT_STAND_I2S 
    //.communication_format = (i2s_comm_format_t) (0X03), //I2S_COMM_FORMAT_STAND_MSB 
    //.communication_format = (i2s_comm_format_t) (0x02), //I2S_COMM_FORMAT_I2S |I2S_COMM_FORMAT_I2S_LSB 
    //.communication_format = (i2s_comm_format_t) (0x04), //I2S_COMM_FORMAT_PCM
    .intr_alloc_flags = 0, // default interrupt priority
    .dma_buf_count = 16,
    .dma_buf_len = 64,
    .use_apll = false
    static const i2s_pin_config_t pin_config = {
        .bck_io_num = 26,
        .ws_io_num = 25,
        .data_out_num = 21,
        .data_in_num = I2S_PIN_NO_CHANGE
    //i2s_set_pin(i2s_num, pin_config);
    void loop() {
    For the Teensy config, I've tried it with an I2S/I2S2 configuration as well as a TDM/I2S2 configuration and I get the same results. I get sound from the bluetooth sink, but it sounds high pitched (chipmunk). When I set the bitrate to 16 bit audio I get loud noise, when I set it to 32 bit audio it's high pitched but it is audible.

    If you have any interest in getting this working it's a pretty simple setup. Run a passthrough USB sketch on teensy, confirm sound, then add your I2S2 object (and a pair of mixers) to the project and hook up the 3 I2S lines (.bck_io_num = 26, .ws_io_num = 25 (lrclk), .data_out_num = 21). Here is some sample code for the Teensy...

    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    // GUItool: begin automatically generated code
    AudioInputI2S2           i2s2_1;         //xy=413.88890075683594,363.88888931274414
    AudioInputUSB            usb1;           //xy=420.8888854980469,282.99999886751175
    AudioMixer4              mixer1;         //xy=623.8888854980469,296.8888854980469
    AudioMixer4              mixer2;         //xy=627.8888320922852,383.8888854980469
    AudioOutputI2S           i2s1;           //xy=797.8889083862305,277.99999237060547
    AudioConnection          patchCord1(i2s2_1, 0, mixer2, 0);
    AudioConnection          patchCord2(i2s2_1, 1, mixer1, 1);
    AudioConnection          patchCord3(usb1, 0, mixer1, 0);
    AudioConnection          patchCord4(usb1, 1, mixer2, 1);
    AudioConnection          patchCord5(mixer1, 0, i2s1, 0);
    AudioConnection          patchCord6(mixer2, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=565.8888702392578,518.0000267028809
    // GUItool: end automatically generated code
    void setup() {                
    void loop() {
      // read the PC's volume setting
      float vol = usb1.volume();
      // scale to a nice range (not too loud)
      // and adjust the audio shield output volume
      if (vol > 0) {
        // scale 0 = 1.0 range to:
        //  0.3 = almost silent
        //  0.8 = really loud
        vol = 0.3 + vol * 0.5;
      // use the scaled volume setting.  Delete this for fixed volume.
    // Teensyduino 1.35 & earlier had a problem with USB audio on Macintosh
    // computers.  For more info and a workaround:

    Does anyone have any advice on how to further debug this? From experience debugging my other modules I'm certain it is a data format/timing issue...

    Thanks for your input!

  3. #3
    Senior Member
    Join Date
    Jun 2018
    I also wanted to add the link to the ESP32 library for Arduino, which is also required.

  4. #4

    I also use an ESP32 as A2DP sink and I also connected it to a Teensy 4.0 via I2S. My setup is working without any problems. So we could search for differences in order to find out what's going on. The only problem is that I am not using the original teensy audio library. But you could compare the I2S- configurations of my Teensy and my ESP32. At my setup the ESP32 is the I2S master and the Teensy the slave. But I would ignore this difference for now.
    The code, that runs on my ESP32 is based on this a2dp sink example:
    The I2S configruation that I use is:
        i2s_config_t i2s_config = {
            .mode = I2S_MODE_MASTER | I2S_MODE_TX,                                  // Only TX
            .sample_rate = 44100,
            .bits_per_sample = 16,
            .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
            .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
            .dma_buf_count = 6,
            .dma_buf_len = 128, //60
            .use_apll = 1,
            .intr_alloc_flags = 0,                                                  //Default interrupt priority
            .tx_desc_auto_clear = true                                              //Auto clear tx descriptor on underflow
    The I2S configuration at the Teensy is:
    	uint8_t noBitsM1=noBits-1;  //noBits=16 
    	I2S1_TCSR &=~I2S_TCSR_TE;
    	I2S1_RCSR &=~I2S_RCSR_RE;
    	CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
    	CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
    	IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
    	IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872
    	// configure transmitter
    	I2S1_TMR = 0;
    	I2S1_TCR1 = I2S_TCR1_RFW(1);  // watermark at half fifo size
    	I2S1_TCR2 = I2S_TCR2_SYNC(1)	//1...transmitter is running synchronously with receiver, 0.. running asynchronously
    				| I2S_TCR2_BCP;		//Bit clock polarity: active low Todo: check
    	I2S1_TCR3 = 0;	// disable all transmit channels for now, I2S_TCR3_TCE;		//Transmit channel enable
    	I2S1_TCR4 = I2S_TCR4_FRSZ(1)	//two words per frame
    				| I2S_TCR4_SYWD(noBitsM1)	//sync width:noBitsM1 -> (noBitsM1+1)bit = 1 word
    				| I2S_TCR4_MF		//most significant bit first
    				| I2S_TCR4_FSE		//Frame sync asserts one bit before the first bit of the frame
    				| I2S_TCR4_FSP;		//frame sync polarity: frame sync is active low
    	I2S1_TCR5 = I2S_TCR5_WNW(noBitsM1)	// word N width
    				| I2S_TCR5_W0W(noBitsM1)	// word 0 width
    				| I2S_TCR5_FBT(31);	//first bit shifted
    	// configure receiver
    	I2S1_RMR = 0;
    	I2S1_RCR1 = I2S_RCR1_RFW(1);
    	I2S1_RCR2 = I2S_RCR2_SYNC(0)	//1...receiver is running synchronously with transmitter, 0.. running asynchronously
    				| I2S_TCR2_BCP;	//Bit Clock is active low with drive outputs on falling edge and sample inputs on rising edge
    	uint32_t enableChannel=I2S_RCR3_RCE;
    	for (uint8_t i =0; i < MAX_NO_I2S1CHANNELS/2; i++){
    		I2S1_RCR3 |= enableChannel;
    	I2S1_RCR4 = I2S_RCR4_FRSZ(1)
    				| I2S_RCR4_SYWD(noBitsM1)
    				| I2S_RCR4_MF
    				| I2S_RCR4_FSE
    				| I2S_RCR4_FSP;
    	I2S1_RCR5 = I2S_RCR5_WNW(noBitsM1)
    				| I2S_RCR5_W0W(noBitsM1)
    				| I2S_RCR5_FBT(31);
    I hope that helps at debugging.


  5. #5
    Senior Member
    Join Date
    Jun 2018
    Hi Alex,

    Thanks, that definitely helps. Does anyone know if I2S2 (in2, out2) can run in slave mode while the I2S1 (in1, out1a, ...) runs in master mode? The audio design tool only has an object for I2Sslave_1. I suspect that there is some reason why running this setup with the Teensy as the Master is causing it not to jive.


  6. #6
    You wrote that configured the ESP32 to output 16bit data. Have you also changed the I2S input of the Teensy to 16bit? I also tried to send 32bit samples from the ESP32 to the Teensy, but there was a problem at the ESP32 (can't remember, it's already some time ago). In the end I gave up and sticked to 16bit data and changed the I2S configuration of the Teensy.

  7. #7
    Senior Member
    Join Date
    Jun 2018
    Have you also changed the I2S input of the Teensy to 16bit?
    I thought Teensy was always operating at 16bit/44100? How do I do this?

  8. #8
    That's right. The teensy audio library runs at 16bit/44.1kHz. But the I2S inputs are configured to receive 32bit data. You would need to write your own AudioInputI2S2 to change that. Basically you can copy the original AudioInputI2S2 + add 'AudioOutputI2S2::config_i2s(void)' from AudioOutputI2S2 to your class. In config_i2s you would need to replace 32 with 16 at I2S_RCR4_SYWD((32-1)), I2S_RCR5_WNW((32-1)) and I2S_RCR5_W0W((32-1)) (not at I2S_RCR5_FBT((32-1)). Then it might work. This configuration at least works at my setup.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts