MoonModules / WLED

Control WS2812B and many more types of digital RGB LEDs with an ESP8266 or ESP32 over WiFi! MoonModules adds features on top of upstream.
https://mm.kno.wled.ge
European Union Public License 1.2
219 stars 68 forks source link

Analog Mic not working on ESP32, works with AnalogRead, though. #10

Closed Funkelfetisch closed 1 year ago

Funkelfetisch commented 1 year ago

What happened?

I tried a simple sketch in the Arduino IDE:

Serial.println(analogRead(35));

which does output seeminly correct (though super noisy) values. If I blow into the microphone I can clearly see it.

Now, if I try to use this fork, the I2S ADC stuff doesn't seem to work. and is always at max. I cannot figure out why.

(micReal always stays at 512 VolumeSmth goes to 0 at some random point in time, DC_Level counts up to 512 and stays there. )

I added the "using audio input" line to make sure, but ADC1_CH7 seems correct.

How do I debug this?

To Reproduce Bug

use my custom PCB :/

Expected Behavior

work

Install Method

Self-Compiled or other

What version/release of MM WLED?

WLEDMM_0.14.0.10 esp32_4MB_max, build 2212060

Which microcontroller/board are you seeing the problem on?

ESP32

Relevant log/trace output

---WLED 0.14.0.10 2212060 INIT---
 WLEDMM_0.14.0.10 esp32_4MB_max, build 2212060.

Usermods setup ...
Finding temperature pin...
temperature usermod initialized.
Starting display.
AR: Analog Microphone (left channel only).
AR: Using audio input: 7
AR: sound input driver initialized successfully.FFT started on core: 
0
MPU6050 connection failed
mpu6050: DMP Initialization failed (code 1)
Ada

GPIO    | Assigned to           | Info
--------|-----------------------|------------
i/o   0   Button                  
i/o   1   ./.                     Serial TX
i/o   2   ./.                     
i/o   3   ./.                     Serial RX
i/o   4   ./.                     
i/o   5   ./.                     
i/o  12   ./.                     
i/o  13   ./.                     
i/o  14   ./.                     
i/o  15   usermod (UM)            
i/o  16   ./.                     (default) LED pin
i/o  17   ./.                     
i/o  18   Temperature (UM)        (default) SPI SLK  / SCK
i/o  19   ./.                     (default) SPI POCI / MISO
i/o  20   ./.                     
i/o  21   I2C (hw)                (default) I2C SDA
i/o  22   I2C (hw)                (default) I2C SCL
i/o  23   ./.                     (default) SPI PICO / MOSI
i/o  25   ./.                     
i/o  26   LEDs (digital)          
i/o  27   ./.                     
i/o  32   ./.                     
i/o  33   ./.                     
in   34   ./.                     
in   35   AudioReactive (UM)      
in   36   ./.                     
in   37   ./.                     
in   38   ./.                     
in   39   ./.                     
WLED initialization done.

Ada
No WiFi connection configured.
Opening access point WLED-AP
micReal:512.00  volumeSmth:8.54 DC_Level:0.04
micReal:512.00  volumeSmth:169.26       DC_Level:0.92
micReal:512.00  volumeSmth:250.26       DC_Level:2.00
micReal:512.00  volumeSmth:254.55       DC_Level:3.12
micReal:512.00  volumeSmth:254.94       DC_Level:4.11
micReal:512.00  volumeSmth:255.00       DC_Level:5.26
micReal:512.00  volumeSmth:255.00       DC_Level:6.38
micReal:512.00  volumeSmth:255.00       DC_Level:7.49
micReal:512.00  volumeSmth:255.00       DC_Level:8.55
micReal:512.00  volumeSmth:255.00       DC_Level:9.66
micReal:512.00  volumeSmth:255.00       DC_Level:10.72
...
micReal:512.00  volumeSmth:53.67        DC_Level:501.92
micReal:512.00  volumeSmth:53.59        DC_Level:501.94
micReal:512.00  volumeSmth:53.52        DC_Level:501.96
micReal:512.00  volumeSmth:53.44        DC_Level:501.98
micReal:512.00  volumeSmth:52.71        DC_Level:502.00
micReal:512.00  volumeSmth:34.41        DC_Level:502.03
micReal:512.00  volumeSmth:16.12        DC_Level:502.05
micReal:512.00  volumeSmth:0.00 DC_Level:502.07
micReal:512.00  volumeSmth:0.00 DC_Level:502.09
micReal:512.00  volumeSmth:0.00 DC_Level:502.11
micReal:512.00  volumeSmth:0.00 DC_Level:502.14
micReal:512.00  volumeSmth:0.00 DC_Level:502.16
micReal:512.00  volumeSmth:0.00 DC_Level:502.18
micReal:512.00  volumeSmth:0.00 DC_Level:502.20

Anything else?

No response

Code of Conduct

Funkelfetisch commented 1 year ago

I narrowed it down! An integer overflow happens in the postProcessSample() method in audio_source.h.

If I do this:

`

      // line 362
    // Store samples in sample buffer and update DC offset
    for (int i = 0; i < num_samples; i++) {
      newSamples[i] = postProcessSample(newSamples[i]);  // perform postprocessing (needed for ADC samples)
      newSamples[i] = newSamples[i] + 33554431;  // HERE THIS I MEAN!! 
      // Serial.println(newSamples[i]);

      float currSample = 0.0f;

`

it's fixed! postProcessSample mostly returned -33554431, except when yelling into the mic, then it got closer to 0. Now it's zeroed in at zero, with positive bursts.

Now WLED appears to be a bit crashy, though, sometimes everything just freezes.

Will spend a few more hours tomorrow to find what's actually causing the overflow.

Weird that I appear to be the only one with this issue!

softhack007 commented 1 year ago

Errm .... I have to look into this. Your fix does not seems right though, because there should be no possibility for overflows.

postProcessSample() does a conversion from unsigned long to signed long, and filters out some rogue samples (the ADC unit sometimes injects them erroneously). What looks like an overflow to you could simply be the conversion process from unsigned to signed, which may look like an overflow when the signal goes from positive to negative.

Edit: your magic number is actually around 512 * 65536, whis is in line with the mic_real value you see. -512 is not a big problem (adc samples go from -2048 to +2047 -512 to 511 after conversion to signed), it just means that your mic has a bad negative DC offset but the software can adapt to that.

softhack007 commented 1 year ago

Audiosource getsamples works on 32bit samples, which are basically "blown up" by 16bit and later scaled down by 18bit.

To simplify debugging, you could compile with -D I2S_USE_16BIT_SAMPLES added to your build_flags. This will skip the "blowing up" part, however reduces sample quality by 2 bits.

softhack007 commented 1 year ago

You could also try this patch, which adds a very had reset procedure (bitbanging hw registers) at the beginning of I2SAdcSource::initialize

https://github.com/atuline/WLED/pull/257/files

The code between SR fork and MM fork is very similar, So you can basically add the few includes and the register write sequence into audio_source.h.

Please let me know if this helps.

softhack007 commented 1 year ago

Another possibility could be to add -D I2S_GRAB_ADC1_COMPLETELY into your build flags. This will switch from one-time to continuous sampling, however will case a lock-up if analogRead() is used anywhere else in WLED.

Please also inform me in case this helps.

Funkelfetisch commented 1 year ago

Thanks for your input! I tried the 16bit, the output of the println after postProcessing is now at mostly -511, when I yell it gets closer to zero.

The animations don't work, though.

I tried the pull request and added it in the initialize method of I2SAdcSource. No change.

I've been using -D I2S_GRAB_ADC1_COMPLETELY since about half of my debugging session. Didn't help.

By the way, if I println before postProcessing, I mostly got "1879048192" After the 16bit change I'm getting 28672.

So to summarize: as I notice, it only seems to work if the "quiet" value is around 0 after postprocessing, or am I misunderstanding something?

edit: if I try to adjust the 1879048192 before postProcessing, after postProcessing the values is again far away from zero, so the animations don't work. I'm not sure if my weird workarounds help in any way ;D

softhack007 commented 1 year ago

So to summarize: as I notice, it only seems to work if the "quiet" value is around 0 after postprocessing, or am I misunderstanding something?

Thanks for the testing and report, this information is actually very helpful because we regularly have people who cannot get analog input to work properly. So if we find something with your setup, it might also solve problems that other people see.

Honestly, If you have a few bucks left in your wallet, the best solution is to buy an I2S digital device like the IMNP441, and leave all the analog trouble behind...

To your question: Actually its not necessary to have "quiet" at 0. In fact, you could try to comment out the post processing line, it should even work without it (but needs more time - about 30-60 seconds - to adjust filters).

There are two things you might check:

  1. To verify that the problem is not in the post processing code, comment out that line temporarily.
  2. As simple electrical test to verify that analog I2S-ADC works, replace the microphone by a potentiometer (around 10kOhm) that works as voltage divider. When you turn it, you should see that the "micdatareal" plot follows (maybe turning direction a 0, as we use absolute values at some point of the processing) This is actually how I test the code to ensure that all possible samples are treated well.
  3. Please check which values you get with the simple analogread() sketch, as you should see similar (but divided by 4) values in WLED. If analogread is ~0 at silence, it means that the mic signal is not centered at 1.8V so the "negative" parts of the waveform will be dropped. That leads exactly to the symptoms you observed, like input appears to be stuck at -511, and animations do not react.

Also I just committed a small change (see https://github.com/MoonModules/WLED/commit/42cc30215978cfec2f37ebb2655bd0c99b6227dc) that enables a new filtering method which possibly can handle your negative DC offset better. Please try. Only downside is that the "potentiometer test" (point 1 above) will not work anymore when the new filter is activated.

softhack007 commented 1 year ago

By the way, if I println before postProcessing, I mostly got "1879048192" After the 16bit change I'm getting 28672.

This is expected - I2S-ADC samples have the channel number encoded in the high 4 bits, which is 28672 as you use ADC channel 7.

Funkelfetisch commented 1 year ago

AnalogRead is indeed around 0.

I tried


// i2s config for using the internal ADC
i2s_config_t adcI2SConfig = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
    .sample_rate = 44100,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
        .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = 1024,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0};

// i2s config for reading from left channel of I2S
i2s_config_t i2sMemsConfigLeftChannel = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 32000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = 1024,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0};

// i2s pins
i2s_pin_config_t i2sPins = {
    .bck_io_num = I2S_PIN_NO_CHANGE,
    .ws_io_num = I2S_PIN_NO_CHANGE,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = I2S_PIN_NO_CHANGE};

for the https://github.com/atomic14/esp32_audio i2s_sampling thingy.

This is the result: https://filebin.net/a8rb1ynt9l6xwsnp/adc.raw Can be played with ffplay -f s32le -ar 40k -ac 1 adc.raw Quite shitty quality.

Problem for me is that I have 240 pcs of a custom designed PCB with esp32 and analog microphone - so no choice but to make it work as-is: schematic

Funkelfetisch commented 1 year ago

I tried without postProcessing now, it does work somehow, but not a great result, visually.

softhack007 commented 1 year ago

Also I just committed a small change (see https://github.com/MoonModules/WLED/commit/42cc30215978cfec2f37ebb2655bd0c99b6227dc) that enables a new filtering method which possibly can handle your negative DC offset better.

Please restore postprocessing, and try with my latest commit - I think that is the best chance to get a meaningful signal out of the hardware you have.

Also "einen schönen Gruss" to whoever designed the pcbs. The microphone part is definitely crap.

softhack007 commented 1 year ago

I just enhanced the miclogger a bit, so you can now see the real waveform. I expect that in your case, it will look like the lower part got cut away (blue line flat).

The enhanced plot looks like this (notice red+blue lines) image

softhack007 commented 1 year ago

Just looked at the pcb design - definitly supports my assumption. There is a capacitor that filters away any existing DC, so after the cap the signal could be like -1V ... 1V. ESP32 cannot sample negative voltages, so the part below 0V is lost, and a lot of noise is added. I think it could be repaired by adding to resistors directly before the esp pin (one to GND the other to 3.3v), to re-center the signal at +1.8v.

Not sure if ESP32 can really take negative voltages on an analog pin, without causing damage at least in long term. So be careful and cross-check the esp32 datasheet. Maybe a diode could help protect the esp pin - but that will also add another source of noise.

Funkelfetisch commented 1 year ago

oh damn. Unfortunately, they are already at >100 customers without working sound-to-light; so I'll just have to keep my fingers crossed they won't fail.

I tried the latest commit - it works! but weirdly with about 1 second delay.

I say BLA, and the light goes on 1 second later :D

Funkelfetisch commented 1 year ago

what tool are you using to graph?

softhack007 commented 1 year ago

what tool are you using to graph?

Oh that was just serial plotter from arduino IDE. You just start arduino IDE, connect the esp32 and select the right port. Then use the tools drop-down and select "serial plotter". It will draw a graphic from what the esp is sending on serial.

Funkelfetisch commented 1 year ago

Ok nice that works well.

image

Everytime I resume the music, it immediately shows on the graph, yet the lights go on later. If that info helps?

softhack007 commented 1 year ago

I tried the latest commit - it works! but weirdly with about 1 second delay.

👍

Some delay is expected, but not that much. Could be you have to reduce the "squelch" setting (audioreactive UM) because this is the threshold where effects start to react.

softhack007 commented 1 year ago

Also it might help to uncheck "sound dynamics limiter". And don't forget to switch on AGC. software AGC in audioreactive that is.

Funkelfetisch commented 1 year ago

in silence, the volumeSmoothing seems to overshoot

image

softhack007 commented 1 year ago

Then you have to increase squelch again , until it stays flat in silence. Looks like squelch around 30-50 could work.

Funkelfetisch commented 1 year ago

ah, I hadn't touched squelch, it was at 10 the whole time. AGC was set to vivid the whole time.

Also the delay was caused by the animation "Waterfall". It works instantly with NoiseFire :)! Thank you for your effort, awesome work!

softhack007 commented 1 year ago

Was fun, at least now I have one more idea why sometimes analog in does not work.

softhack007 commented 1 year ago

Also the delay was caused by the animation "Waterfall".

I guess that all "frequency reactive" effects (1/8th note icon) will have problems, as consequence of the noise in your input. But volume reactive effects (1/4th note) should work better.