Skip to content

Commit a9d55a6

Browse files
committed
esp32s3 and pdm mic proof-of-concept
1 parent 79715f8 commit a9d55a6

File tree

5 files changed

+59
-94
lines changed

5 files changed

+59
-94
lines changed

noisemeter-device/board.h

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,13 @@
2828
#if defined(BOARD_ESP32_PCB)
2929

3030
/** Pin number for the board's LED. */
31-
#define PIN_LED1 (0)
32-
/** Pin number for the board's second LED.
33-
* @deprecated No longer used; production board only has one LED. */
34-
#define PIN_LED2 (3)
31+
#define PIN_LED1 GPIO_NUM_0
3532
/** Pin number for the board's factory reset button. */
36-
#define PIN_BUTTON (1)
37-
/** Pin number for the microphone's WS pin. */
38-
#define I2S_WS (4)
33+
#define PIN_BUTTON GPIO_NUM_1
3934
/** Pin number for the microphone's clock pin. */
40-
#define I2S_SCK (5)
35+
#define I2S_SCK GPIO_NUM_4
4136
/** Pin number for the microphone's data out pin. */
42-
#define I2S_SD (6)
37+
#define I2S_SD GPIO_NUM_5
4338

4439
/** I2S peripheral instance to be used. */
4540
#define I2S_PORT I2S_NUM_0
@@ -48,7 +43,7 @@
4843
#define I2S_FORMAT I2S_CHANNEL_FMT_ONLY_LEFT
4944

5045
/** Serial instance to use for logging output. */
51-
#define SERIAL USBSerial
46+
#define SERIAL Serial
5247

5348
#include <HWCDC.h>
5449
extern HWCDC USBSerial;

noisemeter-device/noisemeter-device.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ void setup()
103103

104104
SERIAL.begin(115200);
105105

106-
if (xTaskCreate(measurementHandler, "dba", 1024, nullptr,
106+
if (xTaskCreate(measurementHandler, "dba", 2048, nullptr,
107107
uxTaskPriorityGet(nullptr), &measurementTask) == pdFAIL)
108108
{
109109
SERIAL.println("xTaskCreate failed!");
@@ -245,7 +245,7 @@ void measurementHandler(void *)
245245

246246
while (1) {
247247
if (const auto db = SPL.readMicrophoneData(); db) {
248-
packets.front().add(*db);
248+
//packets.front().add(*db);
249249
printReadingToConsole(*db);
250250
}
251251
}

noisemeter-device/spl-meter.cpp

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,75 +27,71 @@ static constexpr auto SAMPLES_LEQ = SPLMeter::SAMPLE_RATE * LEQ_PERIOD;
2727
/** Specifies the type of weighting to use for decibel calculation: dBA, dBC, or None/Z. */
2828
static constexpr auto& WEIGHTING = A_weighting;
2929
/** Specifies the microphone's equalization filter. See pre-defined filters or set to 'None'. */
30-
static constexpr auto& MIC_EQUALIZER = SPH0645LM4H_B_RB;
30+
static constexpr auto& MIC_EQUALIZER = None;
3131

3232
/** Valid number of bits in a received I2S data sample. */
33-
static constexpr auto MIC_BITS = 24u;
33+
static constexpr auto MIC_BITS = 16u;
3434
/** dBFS value expected at MIC_REF_DB (Sensitivity value from datasheet). */
35-
static constexpr auto MIC_SENSITIVITY = -26.f;
35+
static constexpr auto MIC_SENSITIVITY = -36.f;
3636
/** Value at which point sensitivity is specified in datasheet (dB). */
3737
static constexpr auto MIC_REF_DB = 94.f;
3838
/** Acoustic overload point (dB). */
39-
static constexpr auto MIC_OVERLOAD_DB = 120.f;
39+
static constexpr auto MIC_OVERLOAD_DB = 130.f;
4040
/** Noise floor (dB). */
41-
static constexpr auto MIC_NOISE_DB = 29.f;
41+
static constexpr auto MIC_NOISE_DB = 30.f;
4242
/** Linear calibration offset to apply to calculated decibel values. */
4343
static constexpr auto MIC_OFFSET_DB = 0.f;
4444

4545
/** Reference amplitude level for the microphone. */
4646
static constexpr auto MIC_REF_AMPL = std::pow(10.f, MIC_SENSITIVITY / 20.f) * ((1 << (MIC_BITS - 1)) - 1);
4747

48-
const i2s_config_t SPLMeter::i2s_config = {
49-
mode: i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
50-
sample_rate: SAMPLE_RATE,
51-
bits_per_sample: i2s_bits_per_sample_t(SPLMeter::SAMPLE_BITS),
52-
channel_format: I2S_FORMAT,
53-
communication_format: i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
54-
intr_alloc_flags: ESP_INTR_FLAG_LEVEL1,
55-
dma_buf_count: 32,
56-
dma_buf_len: SPLMeter::SAMPLES_SHORT / 16u,
57-
use_apll: true,
58-
tx_desc_auto_clear: false,
59-
fixed_mclk: 0,
60-
mclk_multiple: I2S_MCLK_MULTIPLE_DEFAULT,
61-
bits_per_chan: I2S_BITS_PER_CHAN_DEFAULT,
62-
};
63-
64-
const i2s_pin_config_t SPLMeter::pin_config = {
65-
mck_io_num: -1, // not used
66-
bck_io_num: I2S_SCK,
67-
ws_io_num: I2S_WS,
68-
data_out_num: -1, // not used
69-
data_in_num: I2S_SD
70-
};
71-
72-
constexpr std::int32_t SPLMeter::micConvert(std::int32_t s)
73-
{
74-
return s >> (SAMPLE_BITS - MIC_BITS);
75-
}
76-
7748
void SPLMeter::initMicrophone() noexcept
7849
{
79-
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
80-
i2s_set_pin(I2S_PORT, &pin_config);
81-
82-
// Discard first block, microphone may need time to startup and settle.
83-
i2sRead();
50+
i2s_chan_handle_t rx_chan;
51+
52+
/* Setp 1: Determine the I2S channel configuration and allocate RX channel only
53+
* The default configuration can be generated by the helper macro,
54+
* but note that PDM channel can only be registered on I2S_NUM_0 */
55+
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
56+
ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, nullptr, &rx_chan));
57+
58+
/* Step 2: Setting the configurations of PDM RX mode and initialize the RX channel
59+
* The slot configuration and clock configuration can be generated by the macros
60+
* These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM RX mode.
61+
* They can help to specify the slot and clock configurations for initialization or re-configuring */
62+
i2s_pdm_rx_config_t pdm_rx_cfg = {
63+
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
64+
/* The data bit-width of PDM mode is fixed to 16 */
65+
.slot_cfg = I2S_PDM_RX_SLOT_PCM_FMT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
66+
.gpio_cfg = {
67+
.clk = I2S_SCK,
68+
.din = I2S_SD,
69+
.invert_flags = { .clk_inv = false, },
70+
},
71+
};
72+
73+
ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg));
74+
75+
/* Step 3: Enable the rx channels before reading data */
76+
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
77+
i2s_handle = rx_chan;
8478
}
8579

8680
std::optional<float> SPLMeter::readMicrophoneData() noexcept
8781
{
8882
i2sRead();
8983

90-
// Convert (including shifting) integer microphone values to floats,
91-
// using the same buffer (assumed sample size is same as size of float),
92-
// to save a bit of memory
93-
for (auto& s : samples)
94-
s.f = micConvert(s.i);
84+
// I2S samples are 16-bit integers stored into samples's memory.
85+
// These need to be expanded into 32-bit floats for processing.
86+
// Iterate in reverse to avoid overwriting data.
87+
auto src = reinterpret_cast<const int16_t *>(samples.data()) + samples.size();
88+
auto dst = samples.data() + samples.size();
89+
for (int i = 0; i < samples.size(); i++)
90+
*--dst = *--src;
9591

9692
// Apply equalization and calculate Z-weighted sum of squares,
9793
// writes filtered samples back to the same buffer.
98-
auto fptr = &samples[0].f;
94+
const auto fptr = samples.data();
9995
const auto sum_sqr_SPL = MIC_EQUALIZER.filter(fptr, fptr, samples.size());
10096

10197
// Apply weighting and calucate weigthed sum of squares
@@ -134,7 +130,8 @@ void SPLMeter::i2sRead() noexcept
134130
//
135131
// Data is moved from DMA buffers to our 'samples' buffer by the driver ISR
136132
// and when there is requested ammount of data, task is unblocked
137-
size_t bytes_read;
138-
i2s_read(I2S_PORT, samples.data(), samples.size() * sizeof(samples[0]), &bytes_read, portMAX_DELAY);
133+
size_t bread;
134+
(void)bread;
135+
i2s_channel_read(i2s_handle, samples.data(), samples.size() * sizeof(int16_t), &bread, portMAX_DELAY);
139136
}
140137

noisemeter-device/spl-meter.h

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
#include <array>
2323
#include <cstdint>
24-
#include <driver/i2s.h>
24+
#include <driver/i2s_pdm.h>
2525
#include <optional>
2626

2727
/**
@@ -43,49 +43,22 @@ class SPLMeter
4343
std::optional<float> readMicrophoneData() noexcept;
4444

4545
private:
46-
/**
47-
* During processing, microphone samples can either be ints or floats.
48-
* Since sample data takes up a lot of memory, a union is used to reuse
49-
* the buffer during processing.
50-
*/
51-
union sample_t {
52-
/** Raw integer reading from the microphone. */
53-
std::int32_t i;
54-
/** Floating-point microphone value created during processing. */
55-
float f;
56-
};
57-
// Both types must be the same size to allow for buffer reuse.
58-
static_assert(sizeof(float) == sizeof(std::int32_t));
59-
60-
/** The number of bits in a single microphone sample. */
61-
static constexpr auto SAMPLE_BITS = sizeof(sample_t) * 8u;
6246
/** The number of samples to keep in the sample buffer. */
6347
static constexpr auto SAMPLES_SHORT = SAMPLE_RATE / 8u;
64-
/** I2S peripheral config. */
65-
static const i2s_config_t i2s_config;
66-
/** I2S peripheral pin config. */
67-
static const i2s_pin_config_t pin_config;
6848

6949
/** Buffer to store microphone samples in for reading and processing. */
7050
alignas(4)
71-
std::array<sample_t, SAMPLES_SHORT> samples;
51+
std::array<float, SAMPLES_SHORT> samples;
7252

7353
/** Number of samples included in Leq_sum_sqr accumulation. */
7454
unsigned Leq_samples = 0;
7555
/** Accumulation of sums of squares for decibel calculation. */
7656
float Leq_sum_sqr = 0;
7757

58+
i2s_chan_handle_t i2s_handle;
59+
7860
/** Reads enough samples from the microphone to fill the samples buffer. */
7961
void i2sRead() noexcept;
80-
81-
/**
82-
* Converts a raw microphone sample into a usable number.
83-
* This is primarily a bit shift to discard unused bits in the left-aligned
84-
* 32-bit raw sample.
85-
* @param s Sample value to convert
86-
* @return Converted sample value ready for processing
87-
*/
88-
static constexpr std::int32_t micConvert(std::int32_t s);
8962
};
9063

9164
#endif // SPL_METER_H

platformio.ini

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ build_unflags =
2626
build_flags =
2727
-std=gnu++17
2828
-DNO_GLOBAL_EEPROM
29-
-DNOISEMETER_VERSION=\"0.2.4\"
29+
-DNOISEMETER_VERSION=\"0.3.0\"
3030
-Wall -Wextra
31+
-DUPLOAD_DISABLED
3132

3233
# Optional build flags:
3334
# Print credentials over serial (for debugging):
@@ -38,10 +39,9 @@ build_flags =
3839
# -DUPLOAD_DISABLED
3940

4041
[env:esp32-pcb]
41-
board = esp32-c3-devkitm-1
42+
extra_scripts = build_hooks.py
43+
board = seeed_xiao_esp32s3
4244
board_build.f_cpu = 80000000L
43-
board_build.f_flash = 80000000L
44-
board_build.flash_mode = qio
4545
build_flags = ${env.build_flags} -DBOARD_ESP32_PCB
4646

4747
[env:esp32-breadboard]

0 commit comments

Comments
 (0)