Audio acquisition and analysis with ARM Cortex-M0 and I2S microphone

,

Announced frequency range down to 40 Hz is interesting while the ICS43432 is specified from 50 Hz on.

Yes and No: 50 Hz here is only the -3 dB point on frequency response, the unit actually was measured with down to 10 Hz (@ 20 dB attenuation over response @1kHz) :

image

But: bins over, say, 22 kHz are unusable with this mic. So the 40960 Hz are likely only zipper noise! ;)

1 Like

wow, there’s a lot going on here :) cool!

I am afraid it would take some time for me to get started with the code.

I am not sure if this helps, but maybe we could decide for one or several audio test-snippets and both run our code on it? Then we could compare the FFT outcomes?

I can only imagine to play the file with a PC / mobile phone and to record it with the mic of the hardware so we would have a strong bias (speaker, background noise, .mic). Or we would have to connect the input channel with a I2S audio player to get comparable outputs.

Hi @weef, thanks for this! I hadn’t come across this one. It will be good to have some more source code to look at and get inspired by!

2 Likes

3 posts were merged into an existing topic: Audio acquisition and analysis with ESP32

Reading from I2S using Adafruit_ZeroI2S

Dear @wjmb,

searching for enableRx throughout the repository (and also on the Web) effectively just yields the results Repository search results · GitHub, pointing to the dma_passthrough.ino example file.

This effectively will also pull in the ZeroDMA library, which actually does not sound bad either.

If you want to actually follow this route: My plan would be to produce a minimum working example by stripping all tx-related things from dma_passthrough.ino and then try to mix in some details from zerodma_memcpy.ino to actually synchronize your code with the DMA transfer and being able to actually do something with the data after the transfer completed.

By working on that level, you see that you are more directly working with the hardware there: I believe the ArduinoSound library already implements things like double buffering on top of that (which is where things are currently going wrong with your setup re. the observed starvation/stalling behavior).

On the other hand, you might detour to a completely different route. We will be happy to hear about your next findings and observations.

Cheers,
Andreas.

P.S.: Saying that, reading about DMA I2S callback not triggering at high frequency · Issue #294 · arduino/ArduinoCore-samd · GitHub and having a look at https://github.com/fablabbcn/smartcitizen-kit-audio again, it looks like others are also struggling. So, I’m glad you already opened https://github.com/arduino/ArduinoCore-samd/issues/386 - they might even find something what we are missing ;].

Hi Andreas,

Thanks for the suggestion … though I am not quite sure which parts of dma_passthrough.ino would be applicable to the M0 (an #ifndef captures the situation here the target is not an M4).

Do you think that DMA is needed in the situation where we can serialize getting a sample and analysing it later?

So true, it’s even in the header comments. And to make matters worse, they are even using a different ADC, right?

/*  This example shows how to pass data through using the
 *  ZeroI2S and ZeroDMA libraries.
 *  
 *  This example is for M4 devices only
 * 
 *  Try this with the AK4556 I2S ADC/DAC
 *  https://www.akm.com/akm/en/file/datasheet/AK4556VT.pdf
 */

So, maybe let’s consider this departure as abandoned, right?

Looking at how the sound-pressure-level-bricklet people are doing it on the M0 might yield better rewards (thanks again, @weef!) while watching for updates on the issue you created at https://github.com/arduino/ArduinoCore-samd/issues/386.

I expected already they would not use the Arduino HAL at all and that proved right. The result is concise C code which looks just perfect. Enjoy browsing sound-pressure-level-bricklet/software/src.

This - in particular - sparked my interest when having your current problem in mind:

1 Like

Yes … quite ready to start doing so.

However, one good bit of news: with the Adafruit_ZeroI2S library I managed to get a minimal microphone reading sketch working. The code in that library seems to be really bare bones, reading directly from the buffer without any processing. So, perhaps easy for me to understand.

It also brought me one important insight just now: the R/L channel select pin is left floating on the breakout board that I have. I didn’t realise this so far. With the previous microphone it was pulled to one level, so that, left unconnected, it was a left microphone.

Along the lines of

maybe just one more step back to the beginning: May I humbly ask whether the very basic sketch https://github.com/arduino/ArduinoCore-samd/blob/1.6.20/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino actually works without stalling?

Good to hear! Then, just forget about my last post ;]. Good luck with the next steps with Zero_I2S then.

Yes, that one works like a charm.

Ok, thanks.

When looking at https://gist.github.com/amotl/f0640bdab95453ae652ffd0c2f8fbd7f again: Why exactly are you calling I2S.end(); there again?

You probably explained it already at https://github.com/arduino/ArduinoCore-samd/issues/386 :

I want to begin and end I2S within a function, rather than beginning it once and never ending it.

But still: Why actually?

Because I want to do an audio measurement, say, every half our. In between I’d like to switch off
I2S/microphone for two reasons:

  • In earlier tests I noticed issues with LMIC (mostly when expecting to receive a join accept message from the TTN gateway). Though I haven’t gone into a large depth and verified this, it occurred to me that I2S might mess up timing in LMIC.
  • Power saving during the time in which the microphone is not used.

Probably there are other methods in realising same … I guess …

I see your point. Thanks again.

Also, when reconsidering the code at lines 44-47:

if (!I2S.begin(I2S_PHILIPS_MODE, SAMPLE_RATE, 32)) {
    Serial.println("Failed to initialize I2S!");
    while (1); // do nothing
}

This busy loop also might be the reason for the stalls, right? You might miss that point because you are not calling Serial.flush() there, which will pause your program until the transmit buffer has been flushed to the UART. At least, this is what we already learned painfully when attempting to debug in similar conditions.