Teensy 4.1 porting audio to "SDA" pin to be displayed on an I2C OLED Display.

Not open for further replies.


Teensy 4.1 porting audio to "SDA" pin to be displayed on an I2C OLED Display.

I'm looking to use an SDD1306 OLED (128x128) (although I could use another OLED if that would help) to display wave data as a waveform.
I understand that I'm going to have to sample the audio in order to have the waveform 'sync' on the display.
but (and I'm rather new to Teensy), how can get the Audio signal from my Teensy Audio Shield out the teensy 4.1 SDA pin (pin18) ?
Thanks in advance for your help.
Russ from Ocala, Florida, USA
I suspect there are many parts to your question, and I'm not sure I understand exactly what you are asking.

First of all you can't display raw analog values on a display. I.e. the Teensy is not an oscilliscope directly (though you can run programs that make it act as a scope).

You have to read the audio into digital format. Then you do what ever transformation you want on the digital form. To print out the value on to the display, you have to use a library that takes the digital signal and plots it on the display. You will need to dive into the display library to get more information about this.

To hook up a display there several different ways to connect the display. The two most common methods are I2C and SPI.

In the Teensy 4.1 environment, the main I2C and SPI use certain pins:
  • The main I2C bus uses pins 18 (SDA) and 19 (SCL), plus ground and 3.3v;
  • The main SPI bus uses pins 11 (MOSI), 12 (MISO), and 13 (SCLK), plus possibly several other pins (CS, D/C, and possibly reset);
  • Note for older Teensy 3.x boards, the audio adapter forces you to use alternate pins for SPI, but the Teensy 4.x uses the standard pins.

In general, I2C is slower than SPI. One advantage of I2C is it is a shared bus that doesn't need additional pins to add more devices. Each device has an address, and as long as you don't have devices with the same address, things should work. Note, the audio shield has 2 I2C devices on it that allows the audio library to control the SGTL5000 on the audio adapter.

SPI uses a separate pin (CS) that says you are talking to one particular device (rather than having an address like I2C). That means to add 2 SPI devices, you need to have at least 2 CS pins. SPI devices often have 2 other pins (D/C that speeds up switching between data and command modes, and a reset pin). This can mean you would need to allocate 3 fixed pins plus 3 pins per device on the SPI bus (i.e. 2 devices might need 9 pins).

Note, some of the cheaper 128x128 displays do not have a CS pin. This means that device must be the only SPI device. This might be problematical on the audio shield since the audio shield has 2 other SPI devices (the SD card reader and the flash memory port). Now, you probably won't be using either of these on the 4.1, since the 4.1 has a faster micro SD card reader and pads for soldering the flash memory, but I don't know if the existence of the other SPI devices would cause problems for those cheap displays without a CS pin.

If you are trying to use several SPI devices at the same time on the same SPI bus, there are some details you probably need to look at. For your example, it does not sound like you might have multiple SPI devices.

In terms of speed, since the Teensy 4.1 is rather fast, you can sometimes drive a slow display faster than its specs, and in your code, you may need to slow down the bus. For example, I found in the past with the the Teensy 3.5/3.6 that I needed to limit the SPI display speed to 19Mhz, as the devices just code not keep up if I pushed the speed faster.

Obviously for your example, you would worry about correctness first, and then if it is correct worry about pushing the speed up. I was using a program called uncanny eyes that was trying to drive two OLED displays as fast as possible, with each display displaying an eye pattern. Generally the TFT LCD displays would run faster than the OLED displays (but the OLED displays look better and you can see the image at a wider angle).

I would break down your program into two parts. The first part is to get the audio input, and just print it to the USB serial port. The second part is with the audio input, then graph it to your display.

Note, there is an audio example (Part_3_03_TFT_Display) that reads an audio WAV file and displays it on the ILI9341 display that PJRC sells. Unfortunately, I looked at it, and it looks like it needs some changes for the Teensy 4.1 (it was written for the Teensy 3.2, and unfortunately the pin assignments are different between the 3.x and 4.x series in terms of audio stuff).
thank you so much for your reply. My apologies for not being as clear as i should have been, I suppose I was going for 'possibility of concept' sort of thing and I see that my question was not put forward clearly at all. again, sorry about that.
My project is a Synthesizer build using the T4.1 and it's Audio Shield.
Included in the project (and they are currently working) are:
a MIDI interface (and keyboard)
an SSD1306 I2C OLED display that currently displays both graphics and text.

I want to be able to take the Synth's audio output and internally process it in such a way that I read blocks (periods) of the audio and then output the resulting audio data to the SDA pin of the Teensy and display as an Oscilloscope display output would look.
So SIN, Square, Sawtooth, etc would display as such in a 1 or 2 period display, showing their frequencies & amplitudes increase/decrease. It would also show complex waveforms after being processed by code.
I hear what you're saying about the idea that an I2C display may not be up to the task, but since I'm only displaying in the audio range, this may be possible.
below is a not so good, but it has the basic idea of what i want to do. (kudos go out to "ElectroTechnique"):
Here is where the GitHub code resides:
This is an open source program and when looking at the header file named "Oscilloscope.h" he makes use of and SPI oled. the header file makes use of this:
Oscilloscope(void) : AudioStream(1, inputQueueArray)

I get the feeling that, although I'm new to all of this) this may the direction I should be looking in in order to capture a sample of audio data and the process it in order to send it to the OLED.

Thanks again, and I hope this helps clarify what I'm working towards. However this goes, I will have to do a lot of studying in order to catch up to speed in my coding abilities, but I just want to go down the correct 'rabbit-hole' in order to get the results I'd like to attain.

Russ from Ocala, Florida, USA
The audio library provides "queue" objects which are meant to allow your program to obtain raw audio samples. You'll need this one:


Probably the best place to start is the Recorder example which writes the samples to a SD card. Maybe run that and hear it work (listen to the files it records), so you know you're at least getting the audio data.

Then you'll delete all the SD card stuff and replace it with your own code. Easy, right?

There is no way you'll be able to draw pixels for every audio sample and update that little OLED. The I2C protocol is just too slow. Maybe you will choose to use some or all samples to update a frame buffer image in RAM, and then draw that image every 1/10 or 1/15th of a second. Maybe you'll write code to look for the first negative to positive change in samples and start rendering there (roughly what a normal oscilloscope does for rising edge trigger mode). Maybe you'll just use a small group of samples right after that rising edge and discard all the rest, or maybe you'll keep looking for more rising edges and try to render multiple copies of the waveform onto the frame buffer (when better quality oscilloscopes with intensity graded displays do - but probably not practical with a cheap OLED screen which doesn't support grayscale values, only on/off pixels). How you decide to use the audio samples to update the display is up to you.

The good news is the queue object is designed to buffer the audio samples, so your program can do a lengthy step like transmitting the I2C data. During that time, the audio library will store up all the audio buffers, so make sure you give a large number of AudioMemory() because that is where it gets the buffers.

When your program is able to process more of the data, then you'll call the available() function, and readBuffer() to obtain each 128 sample and freeBuffer() to tell the audio library when you've finished using those sample. While the queue is meant to allow your program to process audio in bursts, so you don't need to design every part of your program for low latency, it is critically important that your program calls freeBuffer() at a high enough overall rate that the audio library doesn't run out of buffers. If the library runs out of memory, audio processing will partially or fully stop.
thank you for this reply...
I guess I must have explained my project needs ok, 'cause to me, you hit the nail on the head.
and the send the audio samples (bundles, packets) (to SD card first (in order to see what the data is looking like) is also a great plan. getting to know how to do just that part will take some delving into...
my ideas were right along with yours - basically give myself the availability in the code to tweak the packets of audio data so that whatever OLED or TFT device I'll end up using, will be able to display the data fast enough.

BUTT... <-- the big butt,

After I get the data that is writing out to the SD card working in the proper way,
how do I reroute that processed audio data to the SDA pin of the T4.1? is this something simple to do in coding or are there nodes in the:
Audio System Design Tool for Teensy Audio Library that would help me along that road?

this is so helpful, thank you both!
Russ from Ocala, Florida, USA
You dont "reroute data to the SDA pin". The display wont know what to do with Audio Data, in most cases its a dumb thing that just diplays whatever Bitmap is sent. You need some graphics library that you can use to draw a Picture in a Buffer, which then is sent to the Display. You paint the Picture according to the Audio Data you get from the Screen. Since you mentioned your 1306OLED is already displaying Graphics, you know how to use ist, so the only thing left to do is write some code that converts incomumg Audio Data to a nice Picture.
After I get the data that is writing out to the SD card working in the proper way,
how do I reroute that processed audio data to the SDA pin of the T4.1?

As I tried to explain: "Then you'll delete all the SD card stuff and replace it with your own code. Easy, right?"

I want to reinforce what fdaniels said. You don't "reroute" data like just drawing a connection the design tool, or like how every Star Trek episode's problem gets solved by "rerouting power" simply by touching a screen! It's not a simple & easy thing like that.

You have to write the code which receives those audio buffers. Or just copy the code from the Recorder example. But then what you will actually do with those audio samples is code you must write. There isn't an already-written example. You have to do it. Maybe that's what you would call "reroute", but the reality is a lot of coding, like writing a loop to iterate over the 128 samples and decide which you want to appear on the display, and then calling a display library function like setPixel(x, y) for each sample you want to display.

You don't "reroute" the data (in the sense some sort of ready-made conduit exists like the audio library's patch cords / AudioConnection objects). You only "route" the audio to a queue object in the design tool. Then you use that object from the C++ code in Arduino to access the data. You get the audio sample put in an array from the readBuffer() function. Then your code uses the numbers in that array to draw onto the screen. You have to figure out how to do all that stuff, especially how to deal with you get many samples you don't have time to display. When samples you actually use to update the display and which you just ignore is code you must write based on design and decisions which are fully your responsibility. You have to create that part yourself.

If this still doesn't make sense, start by using that Recorder example. Then after you've recorded some sound files, study that Recorder example. It was written as an example to demonstrate how you get access to the audio data. It's entire reason for being is to serve as an example to help you understand how to get the raw audio data! Use the code, then study the example code.
Paul & fdaniels,
Your explanations have helped me greatly in that I feel that the path I need to take has been shown to me.
from what you have told me, I know that my next step is to run & understand the code of the Recorder program and get to know how the data is put together & written to the SD card. building on that code, I will start experimenting on how fast the system is able to write graphics to the SDD1306 board over I2C and (after finding out its capabilities, i can start building code that will wrap the audio packets up in bundles that can be written out to the SDA (I2C) port in an organized way to visually display a discernable waveform.
thanks for your help so far and I'll keep posting (along with posting the code) as I (hopefully) progress on this project.

Russ from Ocala, Florida, USA
Not open for further replies.