esp32-i2s-slm icon indicating copy to clipboard operation
esp32-i2s-slm copied to clipboard

Stuck reading Led_dB

Open jnrivra opened this issue 3 years ago • 8 comments

Hello there! Thank you so much for this project, its amaizing!

I've been trying to incorporate the code to a multisensor I'm creating but when the ESP reads Serial.printf("%.1f\n", Leq_dB); Serial.println("dB");

it gets stucks reading forever and won't go back to the loop (see attached picture)

any thoughts?

extra: what does ("%.1f\n", Leq_dB); means? I tried reading a Serial.print(Leq_dB); but i get an error

my code is:

void readNoise() {

// Create FreeRTOS queue samples_queue = xQueueCreate(8, sizeof(sum_queue_t));

// Create the I2S reader FreeRTOS task // NOTE: Current version of ESP-IDF will pin the task // automatically to the first core it happens to run on // (due to using the hardware FPU instructions). // For manual control see: xTaskCreatePinnedToCore xTaskCreate(mic_i2s_reader_task, "Mic I2S Reader", I2S_TASK_STACK, NULL, I2S_TASK_PRI, NULL);

sum_queue_t q; uint32_t Leq_samples = 0; double Leq_sum_sqr = 0; double Leq_dB = 0;

// Read sum of samaples, calculated by 'i2s_reader_task' while (xQueueReceive(samples_queue, &q, portMAX_DELAY)) {

// Calculate dB values relative to MIC_REF_AMPL and adjust for microphone reference
double short_RMS = sqrt(double(q.sum_sqr_SPL) / SAMPLES_SHORT);
double short_SPL_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(short_RMS / MIC_REF_AMPL);

// In case of acoustic overload or below noise floor measurement, report infinty Leq value
if (short_SPL_dB > MIC_OVERLOAD_DB) {
  Leq_sum_sqr = INFINITY;
} else if (isnan(short_SPL_dB) || (short_SPL_dB < MIC_NOISE_DB)) {
  Leq_sum_sqr = -INFINITY;
}

// Accumulate Leq sum
Leq_sum_sqr += q.sum_sqr_weighted;
Leq_samples += SAMPLES_SHORT;

// When we gather enough samples, calculate new Leq value
if (Leq_samples >= SAMPLE_RATE * LEQ_PERIOD) {
  double Leq_RMS = sqrt(Leq_sum_sqr / Leq_samples);
  Leq_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(Leq_RMS / MIC_REF_AMPL);
  Leq_sum_sqr = 0;
  Leq_samples = 0;
  
  // Serial output, customize (or remove) as needed

 noise = ("%.1f\n", Leq_dB);
  Serial.print("Decibeles: ");  
  Serial.printf("%.1f\n", Leq_dB);
  Serial.println("dB");


  // Debug only
  //Serial.printf("%u processing ticks\n", q.proc_ticks);
}
}    

}

thanks!!!

jnrivra avatar May 27 '21 03:05 jnrivra

@Xpcker

Serial.printf("%.1f\n", Leq_dB);
Serial.println("dB");

prints a variable to the serial output. You can simply remove or comment those lines.

The code you posted does not compile. The line noise = ("%.1f\n", Leq_dB); is lacking Serial.print probably...

HorstBaerbel avatar Jun 14 '21 11:06 HorstBaerbel

thanks! i managed to fix it !

jnrivra avatar Jun 17 '21 02:06 jnrivra

@Xpcker Good to hear! It makes sense to post how you fixed it so others can possibly solve similar problems. You could then also close your issue I guess...

HorstBaerbel avatar Jun 17 '21 08:06 HorstBaerbel

I'm still working on the project and I'll upload everything, for some reason the esp32 Wifi crashes with the XQueueCreate in the FREERTOS, I'm trying to sync it with a neotimer with no luck, I'll make another issue with that

For the fix, I made a function void readNoise() and I just call it from the loop, The code below is in a readNoise.h file so the code is cleaner

below is the code: @HorstBaerbel =)

jnrivra avatar Oct 14 '21 13:10 jnrivra

#include <driver/i2s.h> #include "sos_IRR_Filter.h"

float noise;

// // Configuration MIC //

#define LEQ_PERIOD 0.1 // second(s) #define WEIGHTING C_weighting // Also avaliable: 'C_weighting' or 'None' (Z_weighting) #define LEQ_UNITS "LAeq" // customize based on above weighting used #define DB_UNITS "dBA" // customize based on above weighting used #define USE_DISPLAY 0

// NOTE: Some microphones require at least DC-Blocker filter #define MIC_EQUALIZER INMP441 // See below for defined IIR filters or set to 'None' to disable #define MIC_OFFSET_DB 3.0103 // Default offset (sine-wave RMS vs. dBFS). Modify this value for linear calibration

// Customize these values from microphone datasheet #define MIC_SENSITIVITY -26 // dBFS value expected at MIC_REF_DB (Sensitivity value from datasheet) #define MIC_REF_DB 94.0 // Value at which point sensitivity is specified in datasheet (dB) #define MIC_OVERLOAD_DB 116.0 // dB - Acoustic overload point #define MIC_NOISE_DB 33 // dB - Noise floor #define MIC_BITS 24 // valid number of bits in I2S data #define MIC_CONVERT(s) (s >> (SAMPLE_BITS - MIC_BITS)) #define MIC_TIMING_SHIFT 0 // Set to one to fix MSB timing for some microphones, i.e. SPH0645LM4H-x

// Calculate reference amplitude value at compile time constexpr double MIC_REF_AMPL = pow(10, double(MIC_SENSITIVITY)/20) * ((1<<(MIC_BITS-1))-1);

// // I2S pins - Can be routed to almost any (unused) ESP32 pin. // SD can be any pin, inlcuding input only pins (36-39). // SCK (i.e. BCLK) and WS (i.e. L/R CLK) must be output capable pins // // Below ones are just example for my board layout, put here the pins you will use // #define I2S_WS 19 #define I2S_SCK 18 #define I2S_SD 23

// I2S peripheral to use (0 or 1) #define I2S_PORT I2S_NUM_0

// // IIR Filters //

// DC-Blocker filter - removes DC component from I2S data // See: https://www.dsprelated.com/freebooks/filters/DC_Blocker.html // a1 = -0.9992 should heavily attenuate frequencies below 10Hz SOS_IIR_Filter DC_BLOCKER = { gain: 1.0, sos: {{-1.0, 0.0, +0.9992, 0}} };

// // Equalizer IIR filters to flatten microphone frequency response // See respective .m file for filter design. Fs = 48Khz. // // Filters are represented as Second-Order Sections cascade with assumption // that b0 and a0 are equal to 1.0 and 'gain' is applied at the last step // B and A coefficients were transformed with GNU Octave: // [sos, gain] = tf2sos(B, A) // See: https://www.dsprelated.com/freebooks/filters/Series_Second_Order_Sections.html // NOTE: SOS matrix 'a1' and 'a2' coefficients are negatives of tf2sos output //

// TDK/InvenSense INMP441 // Datasheet: https://www.invensense.com/wp-content/uploads/2015/02/INMP441.pdf // B ~= [1.00198, -1.99085, 0.98892] // A ~= [1.0, -1.99518, 0.99518] SOS_IIR_Filter INMP441 = { gain: 1.00197834654696, sos: { // Second-Order Sections {b1, b2, -a1, -a2} {-1.986920458344451, +0.986963226946616, +1.995178510504166, -0.995184322194091} } };

// // Weighting filters //

// // A-weighting IIR Filter, Fs = 48KHz // (By Dr. Matt L., Source: https://dsp.stackexchange.com/a/36122) // B = [0.169994948147430, 0.280415310498794, -1.120574766348363, 0.131562559965936, 0.974153561246036, -0.282740857326553, -0.152810756202003] // A = [1.0, -2.12979364760736134, 0.42996125885751674, 1.62132698199721426, -0.96669962900852902, 0.00121015844426781, 0.04400300696788968] SOS_IIR_Filter A_weighting = { gain: 0.169994948147430, sos: { // Second-Order Sections {b1, b2, -a1, -a2} {-2.00026996133106, +1.00027056142719, -1.060868438509278, -0.163987445885926}, {+4.35912384203144, +3.09120265783884, +1.208419926363593, -0.273166998428332}, {-0.70930303489759, -0.29071868393580, +1.982242159753048, -0.982298594928989} } };

// // C-weighting IIR Filter, Fs = 48KHz // Designed by invfreqz curve-fitting, see respective .m file // B = [-0.49164716933714026, 0.14844753846498662, 0.74117815661529129, -0.03281878334039314, -0.29709276192593875, -0.06442545322197900, -0.00364152725482682] // A = [1.0, -1.0325358998928318, -0.9524000181023488, 0.8936404694728326 0.2256286147169398 -0.1499917107550188, 0.0156718181681081] SOS_IIR_Filter C_weighting = { gain: -0.491647169337140, sos: { {+1.4604385758204708, +0.5275070373815286, +1.9946144559930252, -0.9946217070140883}, {+0.2376222404939509, +0.0140411206016894, -1.3396585608422749, -0.4421457807694559}, {-2.0000000000000000, +1.0000000000000000, +0.3775800047420818, -0.0356365756680430} } };

// // Sampling // #define SAMPLE_RATE 48000 // Hz, fixed to design of IIR filters #define SAMPLE_BITS 32 // bits #define SAMPLE_T int32_t #define SAMPLES_SHORT (SAMPLE_RATE / 8) // ~125ms #define SAMPLES_LEQ (SAMPLE_RATE * LEQ_PERIOD) #define DMA_BANK_SIZE (SAMPLES_SHORT / 16) #define DMA_BANKS 32

// Data we push to 'samples_queue' struct sum_queue_t { // Sum of squares of mic samples, after Equalizer filter float sum_sqr_SPL; // Sum of squares of weighted mic samples float sum_sqr_weighted; // Debug only, FreeRTOS ticks we spent processing the I2S data uint32_t proc_ticks; }; QueueHandle_t samples_queue;

// Static buffer for block of samples float samples[SAMPLES_SHORT] attribute((aligned(4)));

// // I2S Microphone sampling setup // void mic_i2s_init() { // Setup I2S to sample mono channel for SAMPLE_RATE * SAMPLE_BITS // NOTE: Recent update to Arduino_esp32 (1.0.2 -> 1.0.3) // seems to have swapped ONLY_LEFT and ONLY_RIGHT channels const i2s_config_t i2s_config = { mode: i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), sample_rate: SAMPLE_RATE, bits_per_sample: i2s_bits_per_sample_t(SAMPLE_BITS), 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: DMA_BANKS, dma_buf_len: DMA_BANK_SIZE, use_apll: true, tx_desc_auto_clear: false, fixed_mclk: 0 }; // I2S pin mapping const i2s_pin_config_t pin_config = { bck_io_num: I2S_SCK,
ws_io_num: I2S_WS,
data_out_num: -1, // not used data_in_num: I2S_SD
};

i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);

#if (MIC_TIMING_SHIFT > 0) // Undocumented (?!) manipulation of I2S peripheral registers // to fix MSB timing issues with some I2S microphones REG_SET_BIT(I2S_TIMING_REG(I2S_PORT), BIT(9));
REG_SET_BIT(I2S_CONF_REG(I2S_PORT), I2S_RX_MSB_SHIFT);
#endif

i2s_set_pin(I2S_PORT, &pin_config);

//FIXME: There is a known issue with esp-idf and sampling rates, see: // https://github.com/espressif/esp-idf/issues/2634 // In the meantime, the below line seems to set sampling rate at ~47999.992Hz // fifs_req=24576000, sdm0=149, sdm1=212, sdm2=5, odir=2 -> fifs_reached=24575996
//NOTE: This seems to be fixed in ESP32 Arduino 1.0.4, esp-idf 3.2 // Should be safe to remove... //#include <soc/rtc.h> //rtc_clk_apll_enable(1, 149, 212, 5, 2); }

// // I2S Reader Task // // Rationale for separate task reading I2S is that IIR filter // processing cam be scheduled to different core on the ESP32 // while main task can do something else, like update the // display in the example // // As this is intended to run as separate hihg-priority task, // we only do the minimum required work with the I2S data // until it is 'compressed' into sum of squares // // FreeRTOS priority and stack size (in 32-bit words) #define I2S_TASK_PRI 4 #define I2S_TASK_STACK 2048 // void mic_i2s_reader_task(void* parameter) { mic_i2s_init();

// Discard first block, microphone may have startup time (i.e. INMP441 up to 83ms) size_t bytes_read = 0; i2s_read(I2S_PORT, &samples, SAMPLES_SHORT * sizeof(int32_t), &bytes_read, portMAX_DELAY);

while (true) { // Block and wait for microphone values from I2S // // Data is moved from DMA buffers to our 'samples' buffer by the driver ISR // and when there is requested ammount of data, task is unblocked // // Note: i2s_read does not care it is writing in float[] buffer, it will write // integer values to the given address, as received from the hardware peripheral. i2s_read(I2S_PORT, &samples, SAMPLES_SHORT * sizeof(SAMPLE_T), &bytes_read, portMAX_DELAY);

TickType_t start_tick = xTaskGetTickCount();

// Convert (including shifting) integer microphone values to floats, 
// using the same buffer (assumed sample size is same as size of float), 
// to save a bit of memory
SAMPLE_T* int_samples = (SAMPLE_T*)&samples;
for(int i=0; i<SAMPLES_SHORT; i++) samples[i] = MIC_CONVERT(int_samples[i]);

sum_queue_t q;
// Apply equalization and calculate Z-weighted sum of squares, 
// writes filtered samples back to the same buffer.
q.sum_sqr_SPL = MIC_EQUALIZER.filter(samples, samples, SAMPLES_SHORT);

// Apply weighting and calucate weigthed sum of squares
q.sum_sqr_weighted = WEIGHTING.filter(samples, samples, SAMPLES_SHORT);

// Debug only. Ticks we spent filtering and summing block of I2S data
q.proc_ticks = xTaskGetTickCount() - start_tick;

// Send the sums to FreeRTOS queue where main task will pick them up
// and further calcualte decibel values (division, logarithms, etc...)
xQueueSend(samples_queue, &q, portMAX_DELAY);

} }

void readNoise() {

// Create FreeRTOS queue samples_queue = xQueueCreate(8, sizeof(sum_queue_t));

// Create the I2S reader FreeRTOS task // NOTE: Current version of ESP-IDF will pin the task // automatically to the first core it happens to run on // (due to using the hardware FPU instructions). // For manual control see: xTaskCreatePinnedToCore xTaskCreate(mic_i2s_reader_task, "Mic I2S Reader", I2S_TASK_STACK, NULL, I2S_TASK_PRI, NULL);

sum_queue_t q; uint32_t Leq_samples = 0; double Leq_sum_sqr = 0; double Leq_dB = 0;

// Read sum of samaples, calculated by 'i2s_reader_task' if (xQueueReceive(samples_queue, &q, portMAX_DELAY)) {

// Calculate dB values relative to MIC_REF_AMPL and adjust for microphone reference
double short_RMS = sqrt(double(q.sum_sqr_SPL) / SAMPLES_SHORT);
double short_SPL_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(short_RMS / MIC_REF_AMPL);

// In case of acoustic overload or below noise floor measurement, report infinty Leq value
if (short_SPL_dB > MIC_OVERLOAD_DB) {
  Leq_sum_sqr = INFINITY;
} else if (isnan(short_SPL_dB) || (short_SPL_dB < MIC_NOISE_DB)) {
  Leq_sum_sqr = -INFINITY;
}

// Accumulate Leq sum
Leq_sum_sqr += q.sum_sqr_weighted;
Leq_samples += SAMPLES_SHORT;

// When we gather enough samples, calculate new Leq value
if (Leq_samples >= SAMPLE_RATE * LEQ_PERIOD) {
  double Leq_RMS = sqrt(Leq_sum_sqr / Leq_samples);
  Leq_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(Leq_RMS / MIC_REF_AMPL);
  Leq_sum_sqr = 0;
  Leq_samples = 0;
  

 noise = ("%.1f\n", Leq_dB);
 Serial.print("Decibels: ");  
 Serial.print(Leq_dB);
 Serial.println(" dB");


}

}  

}

jnrivra avatar Oct 14 '21 13:10 jnrivra

Try a smaller number of queues: samples_queue = xQueueCreate(3, sizeof(sum_queue_t));

And it would be nice if you could format your source code using the "insert code" button. Makes it much more readable.

HorstBaerbel avatar Oct 14 '21 13:10 HorstBaerbel

You could also close the issue yourself, if you got it fixed...

HorstBaerbel avatar Oct 14 '21 13:10 HorstBaerbel

Hey buddy i have the same issue, but when i call it from loop as you said so i get flash reset problem with esp32, is it possible if you could share your code. it would be so helpful

Lat9898 avatar Dec 14 '23 19:12 Lat9898