-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Description
Confirmed working in esp32 idf 5.1 alpha 3.0.0
The INMP441 mems mic is the culprit. The working theory is that when the I2S clock stops the microphone goes into a power down mode and then generates noise during power up when I2S resumes. See final comment for possible work around using LEDC to generate a pseudo clock to keep the device awake.
Example:
Now entering light sleep
Exited light sleep, now outputting sound levels for one second.
vol: 2874
vol: 1315
vol: 579
vol: 887
vol: 764
vol: 587
vol: 399
vol: 270
vol: 231
vol: 134
vol: 50
vol: 68
vol: 50
vol: 31
vol: 38
vol: 36
vol: 41
vol: 35
vol: 33
vol: 47
Board
XIAO ESP32-C3 with battery
Device Description
https://www.amazon.com/dp/B0B94JZ2YF?psc=1&ref=ppx_yo2ov_dt_b_product_details
I'm using Platform io. Here is my ini file
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32c3]
platform = espressif32@^6.2.0
board = seeed_xiao_esp32c3
framework = arduino
; change microcontroller
board_build.mcu = esp32c3
;upload_speed = 115200
; change WiFi firmware
board_build.variant = esp32c3
monitor_speed = 115200
board_build.f_cpu = 80000000L
build_flags =
-D CONFIG_PM_ENABLE
-D CONFIG_PM_USE_RTC
-D CONFIG_PM_LIGHTSLEEP_RTC_OSC_CAL_INTERVAL=8
I have a INMP441 mems mic:
For the life of me I cannot get the IS2 peripheral to power back on quickly. I have to wait about 1/3rd of a second before the microphone stabilizes. I've tried so many different things: changing the clock signal. Changing the board frequency. Nothing seems to work to make the microphone better.
Hardware Configuration
#ifndef _DEFS_H_
#define _DEFS_H_
#include <iostream>
using std::cout;
using std::endl;
#define MAX_ANALOG_READ 1023
#define LED_PIN 9 // Pin is held high on startup.
#define LED_PIN_IS_SINK true
#define LED_0 0 // Led channel name
#define LED_PWM_FREQ 2000
#define LED_PWM_RESOLUTION 14 // Max on ESP32-c3 XIOA
#define TIME_PWM_CYCLE_MS 3 // Flickers at 1ms
#define TIME_PWM_TRANSITION_MS 3 // 60 fps
#define PIN_I2S_WS GPIO_NUM_3 // TODO change this pins
#define PIN_IS2_SD GPIO_NUM_2 // TODO change this pins
#define PIN_I2S_SCK GPIO_NUM_4 // TODO change this pins
#define PIN_AUDIO_PWR GPIO_NUM_5 // TODO change this pins
#define I2S_NUM I2S_NUM_0
// #define IS2_AUDIO_BUFFER_LEN 1024 // max samples for i2s_read
#define IS2_AUDIO_BUFFER_LEN 1024 // max samples for i2s_read
#define AUDIO_BIT_RESOLUTION 16
#define AUDIO_SAMPLE_RATE (44100ul / 1)
#define AUDIO_CHANNELS 1 // Not tested with 2 channels
#define AUDIO_DMA_BUFFER_COUNT 3
#define AUDIO_RECORDING_SECONDS 1
#define TIME_BEFORE_LIGHT_SLEEP_MS 1000
#define LIGHT_SLEEP_TIME_uS uint32_t(1000 * 100) // 100 ms.
#define ASSERT_IMPL(x, msg, file, lineno) \
do \
{ \
if (!(x)) \
{ \
std::cout << "#############\n# ASSERTION FAILED: " << file << ":" << lineno << "\n# MSG: " << msg << "\n#############\n"; \
configASSERT(x); \
} \
} while (false);
#define ASSERT(x, msg) ASSERT_IMPL(x, msg, __FILE__, __LINE__)
#endif // _DEFS_H_
Version
v2.0.9
IDE Name
PlatformIO
Operating System
Max OS 13 on M1
Flash frequency
default
PSRAM enabled
no
Upload speed
115200
Description
I2S has to wait a long time before it stabilizes after a light sleep. I have to throw away the next 12 buffers of 1024, 44100 hz audio data in mono format. I thought it was the microphone but it appears to be the IS2 bus.
Sketch
++
#include <iostream>
#include "audio.h"
#include "defs.h"
#include <Arduino.h>
#include <stdint.h>
#include <driver/i2s.h>
#include "ringbuffer.hpp"
#include "alloc.h"
#include "buffer.hpp"
#include "task.h"
#include <limits>
#include "time.h"
#include <stdio.h>
#include <atomic>
#include "alloc.h"
using namespace std;
#define ENABLE_AUDIO_TASK 0
#define AUDIO_TASK_SAMPLING_PRIORITY 7
#define AUDIO_BUFFER_SAMPLES (AUDIO_RECORDING_SECONDS * AUDIO_SAMPLE_RATE * AUDIO_CHANNELS)
// During power
#define POWER_ON_TIME_MS 85 // Time to power on the microphone according to the datasheet.
#define POWER_OFF_TIME_MS 85 // Time to power off the microphone is 43 ms but we round up.
// Note that during power down, no data should be attempted to be read
// or the ESD diodes will be activated and the microphone will be damaged.
namespace
{
static_assert(AUDIO_BIT_RESOLUTION == 16, "Only 16 bit resolution is supported");
static_assert(AUDIO_CHANNELS == 1, "Only 1 channel is supported");
static_assert(sizeof(audio_sample_t) == 2, "audio_sample_t must be 16 bit");
std::atomic<float> s_loudness_dB;
std::atomic<uint32_t> s_loudness_updated;
int garbage_buffer_count = 0;
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = AUDIO_SAMPLE_RATE,
.bits_per_sample = i2s_bits_per_sample_t(AUDIO_BIT_RESOLUTION),
.channel_format = i2s_channel_fmt_t(I2S_CHANNEL_FMT_ONLY_RIGHT),
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
.intr_alloc_flags = 0,
.dma_buf_count = AUDIO_DMA_BUFFER_COUNT,
.dma_buf_len = IS2_AUDIO_BUFFER_LEN,
.use_apll = false,
//.tx_desc_auto_clear = true,
//.fixed_mclk = 4000000ul,
};
const i2s_pin_config_t pin_config = {
.bck_io_num = PIN_I2S_SCK,
.ws_io_num = PIN_I2S_WS,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = PIN_IS2_SD};
void i2s_audio_init()
{
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
//i2s_zero_dma_buffer(I2S_NUM_0);
//i2s_start(I2S_NUM_0);
}
void i2s_audio_shutdown()
{
//i2s_stop(I2S_NUM_0);
i2s_driver_uninstall(I2S_NUM_0);
}
double audio_loudness_to_dB(double rms_loudness)
{
// This is a rough approximation of the loudness to dB scale.
// The data was taken from the following video featuring brown
// noise: https://www.youtube.com/watch?v=hXetO_bYcMo
// This linear regression was done on the following data:
// DB | LOUDNESS
// ---+---------
// 50 | 15
// 55 | 22
// 60 | 33
// 65 | 56
// 70 | 104
// 75 | 190
// 80 | 333
// This will produce an exponential regression of the form:
// 0.0833 * std::exp(0.119 * x);
// Below is the inverse exponential regression.
const float kCoefficient = 0.119f;
const float kIntercept = 0.0833f;
const float kInverseCoefficient = 1.0f / kCoefficient; // Maybe faster to precompute this.
const float kInverseIntercept = 1.0f / kIntercept;
return std::log(rms_loudness * kInverseIntercept) * kInverseCoefficient;
}
float calc_rms_loudness(const audio_sample_t *samples, size_t num_samples)
{
uint64_t sum_of_squares = 0;
for (size_t i = 0; i < num_samples; ++i)
{
sum_of_squares += samples[i] * samples[i];
}
double mean_square = static_cast<double>(sum_of_squares) / num_samples;
return static_cast<float>(std::sqrt(mean_square));
}
size_t read_raw_samples(audio_sample_t (&buffer)[IS2_AUDIO_BUFFER_LEN])
{
size_t bytes_read = 0;
i2s_event_t event;
uint32_t current_time = millis();
esp_err_t result = i2s_read(I2S_NUM_0, buffer, sizeof(buffer), &bytes_read, 0);
if (result == ESP_OK)
{
if (bytes_read > 0)
{
//cout << "Bytes read: " << bytes_read << endl;
const size_t count = bytes_read / sizeof(audio_sample_t);
return count;
}
}
return 0;
}
bool update_audio_samples()
{
audio_sample_t buffer[IS2_AUDIO_BUFFER_LEN] = {0};
bool updated = false;
while (true) {
size_t samples_read = read_raw_samples(buffer);
if (samples_read <= 0)
{
break;
}
if (garbage_buffer_count > 0) {
--garbage_buffer_count;
continue;
}
updated = true;
float rms = calc_rms_loudness(buffer, samples_read);
s_loudness_dB.store(audio_loudness_to_dB(rms));
s_loudness_updated.store(millis());
}
return updated;
}
bool s_audio_initialized = false;
} // anonymous namespace
void audio_task(void *pvParameters)
{
while (true)
{
// Drain out all pending buffers.
while (update_audio_samples())
{
;
}
delay_task_ms(7);
}
}
void audio_init(bool wait_for_power_on)
{
if (s_audio_initialized)
{
cout << "Audio already initialized." << endl;
return;
}
s_audio_initialized = true;
pinMode(PIN_AUDIO_PWR, OUTPUT);
digitalWrite(PIN_AUDIO_PWR, HIGH); // Power on the IS2 microphone.
i2s_audio_init();
if (wait_for_power_on) {
delay_task_ms(POWER_ON_TIME_MS); // Wait for the microphone to power on.
}
// start a task to read the audio samples using psram
//TaskCreatePsramPinnedToCore(
// audio_task, "audio_task", 4096, NULL, AUDIO_TASK_SAMPLING_PRIORITY, NULL, 0);
#if ENABLE_AUDIO_TASK
xTaskCreatePinnedToCore(
audio_task, "audio_task", 4096, NULL, AUDIO_TASK_SAMPLING_PRIORITY, NULL, 0);
#endif
}
// UNTESTED
void audio_shutdown()
{
//i2s_stop(I2S_NUM_0); // Stop the I2S
//i2s_driver_uninstall(I2S_NUM_0); // Uninstall the driver
s_audio_initialized = false;
}
audio_state_t audio_update()
{
uint32_t start_time = millis();
update_audio_samples();
#if ENABLE_AUDIO_TASK
for (int i = 0; i < 3; i++)
{
vPortYield();
}
#endif
audio_state_t state = audio_state_t(audio_loudness_dB(), s_loudness_updated.load());
return state;
}
float audio_loudness_dB() { return s_loudness_dB.load(); }
// Audio
void audio_loudness_test()
{
Buffer<double> sample_buffer;
sample_buffer.init(32);
cout << "Done initializing audio buffers" << endl;
while (true)
{
// This is a test to see how loud the audio is.
// It's not used in the final product.
audio_sample_t buffer[IS2_AUDIO_BUFFER_LEN] = {0};
size_t samples_read = read_raw_samples(buffer);
if (samples_read > 0)
{
double rms_loudness = calc_rms_loudness(buffer, samples_read);
sample_buffer.write(&rms_loudness, 1);
double avg = 0;
for (size_t i = 0; i < sample_buffer.size(); ++i)
{
avg += sample_buffer[i];
}
avg /= sample_buffer.size();
String loudness_str = String(avg, 3);
// Serial.printf("Avg rms loudness: %s\n", loudness_str.c_str());
float dB = audio_loudness_to_dB(avg);
String dB_str = String(dB, 3);
Serial.printf("dB: %s, loudness: %s\n", dB_str.c_str(), loudness_str.c_str());
// buffer->clear();
sample_buffer.clear();
}
}
}
void audio_enter_light_sleep() {
audio_sample_t buffer[IS2_AUDIO_BUFFER_LEN] = {0};
//i2s_stop(I2S_NUM_0); // Stop the I2S
i2s_audio_shutdown();
pinMode(PIN_I2S_SCK, OUTPUT); // This is all desperation trying to reset pin state so that when I2S restarts it works.
digitalWrite(PIN_I2S_SCK, LOW);
pinMode(PIN_I2S_WS, OUTPUT);
digitalWrite(PIN_I2S_WS, LOW);
pinMode(PIN_IS2_SD, OUTPUT);
digitalWrite(PIN_IS2_SD, LOW);
// i2s_zero_dma_buffer(I2S_NUM_0);
//digitalWrite(PIN_AUDIO_PWR, HIGH);
gpio_hold_en(PIN_I2S_WS);
gpio_hold_en(PIN_IS2_SD);
gpio_hold_en(PIN_I2S_SCK);
gpio_hold_en(PIN_AUDIO_PWR);
}
void audio_exit_light_sleep() {
//delay(8);
i2s_audio_init();
gpio_hold_dis(PIN_I2S_WS);
gpio_hold_dis(PIN_IS2_SD);
gpio_hold_dis(PIN_I2S_SCK);
// gpio_hold_dis(PIN_AUDIO_PWR);
// i2s_start(I2S_NUM_0);
i2s_audio_init();
//delay(160);
//audio_sample_t buffer[IS2_AUDIO_BUFFER_LEN] = {0};
//uint32_t future_time = millis() + 180;
//while (millis() < future_time) {
// read_raw_samples(buffer);
//}
//delay(180);
//delay(POWER_ON_TIME_MS * 2);
garbage_buffer_count = 14; // For some reason it takes a few buffers to get the audio going again.
}
Debug Message
None. But the recorded sound levels are through the roof for the first few buffers then decrease.
Other Steps to Reproduce
No response
I have checked existing issues, online documentation and the Troubleshooting Guide
- I confirm I have checked existing issues, online documentation and Troubleshooting guide.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status