I am trying to connect a PDM microphone to my monitor one without the expansion card. I have connect CLK to A3 and Data to A5 but I am not getting any response from the microphone. Is there a different set of pins I need to use or do I need to declare them in setup?
It should work. Can you share the code you are using? All of it is best, but at least the setup lines for the MicrophonePDM library.
Hey Rick, Here is the code I am using.
#include "Particle.h"
#include "Microphone_PDM.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);
constexpr uint32_t FS_PDM = 32000; // fixed PDM rate
void setup() {
Serial.begin(9600);
Serial.println(">> setup start");
waitFor(Serial.isConnected, 3000);
Serial.println(">> serial OK");
// header
Serial.println("RESULT,A,PDM_OK,PDM_samples");
// init PDM once
auto &mic = Microphone_PDM::instance();
bool ok = mic
.withOutputSize(Microphone_PDM::OutputSize::SIGNED_16)
.withRange (Microphone_PDM::Range ::RANGE_2048)
.withSampleRate(FS_PDM)
.init();
Serial.printf(">> PDM init: %s\n", ok ? "OK" : "FAIL");
if (ok) {
mic.start();
Serial.println(">> PDM started");
}
Serial.println(">> setup done");
}
void loop() {
Serial.println(">> loop start");
// test PDM copy
auto &mic = Microphone_PDM::instance();
static int16_t buf[128];
bool got = mic.copySamples(buf);
Serial.printf(" PDM.copySamples() returned %s", got ? "true" : "false");
if (got) {
Serial.printf(", first sample = %d", buf[0]);
}
Serial.println();
Serial.println(">> loop end\n");
delay(2000);
}```
You also need to set the pins:
bool ok = mic
.withPinCLK(A3)
.withPinDAT(A5)
.withOutputSize(Microphone_PDM::OutputSize::SIGNED_16)
.withRange (Microphone_PDM::Range ::RANGE_2048)
.withSampleRate(FS_PDM)
.init();
I tried that and it is still returning no values. Here is the code I am trying to run which collects data from an analog and a PDM mic. Do you have any other suggestions?
#include "Particle.h"
#include "Microphone_PDM.h"
#include "arduinoFFT.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);
// FFT and sampling parameters
constexpr uint16_t SAMPLES = 512;
constexpr double FS_ANALOG = 8000.0; // MAX4466 rate
constexpr uint32_t FS_PDM = 32000; // PDM mic rate (16k or 32k)
// I/O pins
constexpr int ANALOG_PIN = A4; // MAX4466 output
//constexpr int PDM_DATA_PIN = A5; // MP34DT01 DATA
//constexpr int PDM_CLK_PIN = A3; // MP34DT01 CLK
// Detection band & threshold
constexpr double FMIN = 600.0;
constexpr double FMAX = 1250.0;
constexpr double THRESH = 10.0;
// Buffers & FFTs
double vRealA[SAMPLES], vImagA[SAMPLES];
double vRealP[SAMPLES], vImagP[SAMPLES];
ArduinoFFT<double> FFT_A(vRealA, vImagA, SAMPLES, FS_ANALOG);
ArduinoFFT<double> FFT_P(vRealP, vImagP, SAMPLES, FS_PDM);
// Tracks whether the PDM mic is available
bool pdmReady = false;
void setup() {
Serial.begin(9600);
waitFor(Serial.isConnected, 5000);
// 1) Print the CSV header immediately
Serial.println("RESULT,A_freq,A_amp,P_freq,P_amp");
// 2) Try to init the PDM mic—log but do not block on failure
auto &mic = Microphone_PDM::instance();
pdmReady = mic
.withPinCLK(A3)
.withPinDAT(A5)
.withOutputSize(Microphone_PDM::OutputSize::SIGNED_16)
.withRange (Microphone_PDM::Range ::RANGE_2048)
.withSampleRate(FS_PDM)
.init();
if (pdmReady) {
mic.start();
} else {
Serial.println("⚠️ PDM init failed—continuing without PDM");
}
pinMode(ANALOG_PIN, INPUT);
}
void loop() {
// --- Sample and FFT the analog mic ---
for (uint16_t i = 0; i < SAMPLES; ++i) {
vRealA[i] = analogRead(ANALOG_PIN);
vImagA[i] = 0.0;
delayMicroseconds((unsigned)(1e6/FS_ANALOG));
}
FFT_A.windowing(vRealA, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT_A.compute (vRealA, vImagA, SAMPLES, FFT_FORWARD);
FFT_A.complexToMagnitude(vRealA, vImagA, SAMPLES);
// --- Optionally sample & FFT the PDM mic ---
double peakP = 0.0, freqP = 0.0;
if (pdmReady) {
static int16_t pdmBuf[SAMPLES];
auto &mic = Microphone_PDM::instance();
if (mic.copySamples(pdmBuf)) {
for (int i = 0; i < SAMPLES; ++i) {
vRealP[i] = pdmBuf[i];
vImagP[i] = 0.0;
}
FFT_P.windowing(vRealP, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT_P.compute (vRealP, vImagP, SAMPLES, FFT_FORWARD);
FFT_P.complexToMagnitude(vRealP, vImagP, SAMPLES);
}
}
// --- Find peaks in the target band ---
double peakA = 0.0, freqA = 0.0;
for (uint16_t bin = 0; bin < SAMPLES/2; ++bin) {
double fA = bin * (FS_ANALOG / SAMPLES);
if (fA >= FMIN && fA <= FMAX && vRealA[bin] > peakA) {
peakA = vRealA[bin];
freqA = fA;
}
if (pdmReady) {
double fP = bin * (FS_PDM / SAMPLES);
if (fP >= FMIN && fP <= FMAX && vRealP[bin] > peakP) {
peakP = vRealP[bin];
freqP = fP;
}
}
}
// --- Emit CSV if either sensor sees a strong peak ---
if (peakA > THRESH || (pdmReady && peakP > THRESH)) {
Serial.printf(
"RESULT,%.1f,%.2f,%.1f,%.2f\n",
freqA, peakA,
pdmReady ? freqP : 0.0,
pdmReady ? peakP : 0.0
);
}
delay(200);
}```
Which log messages are you getting?
I am getting good results with the Analog Mic, but the PDM is showing all 0's.
RESULT,609.4,7795.36,0.0,0.00
RESULT,609.4,11506.30,0.0,0.00
RESULT,609.4,12679.54,0.0,0.00
RESULT,812.5,19087.41,0.0,0.00
RESULT,656.2,19361.82,0.0,0.00
RESULT,687.5,11418.29,0.0,0.00
It's not obvious why it's not working. However the PDM doesn't handshake with the microphone, it just toggles the CLK pin appropriately and the microphone toggles the DAT pin according the the PDM protocol rules. Since you're getting all 0s, some possibilities are:
- Bad power (3V3) or ground to the PDM breakout
- Problem with the CLK or DAT lines, or their configuration
- Hardware problem with the microphone
- The library isn't starting PDM DMA properly but not returning an error
- ???
I am not finding any issues with the hardware nor wiring. Do you happened to have any example code that works with the Monitor One? Maybe I missed something in the set up.
I never tested Microphone_PDM on Monitor One but I did test it on nRF52 (Argon) and I can't think of a reason it should not work on Tracker SoM/Monitor One. I'll try to test it later this week on a Monitor One.
Awesome. I appreciate it. I took the expansion board off and wired it directly to the TSOM. not sure if that was a good idea or not.
There are two problems:
- You can't use pin A3 because that's the battery temperature sensor on the Monitor One.
- You need to disable SPI, which defaults to on and prevents using A4 and A5. The built-in SPI peripherals like the IOEX are on SPI1 and will still work.
To disable SPI, add this to setup():
SPI.end();
Tested on the Monitor One. I used Device OS 6.3.3, but the Device OS version shouldn't matter.
I used the Adafruit PDM microphone connected to A4 (CLK) and A5 (DAT) with the following initialization:
int err = Microphone_PDM::instance()
.withPinCLK(A4)
.withPinDAT(A5)
.withOutputSize(Microphone_PDM::OutputSize::SIGNED_16)
.withRange(Microphone_PDM::Range::RANGE_2048)
.withSampleRate(SAMPLE_FREQ)
.init();
I used the a new example that does FFT on the microphone to do DTMF detection, and it worked as expected.
0000029436 [app] INFO: pressed 1
0000058609 [app] INFO: pressed 2
0000060006 [app] INFO: pressed 3
I updated the Microphone_PDM library to 0.0.5. It fixes a SRAM overflow on RTL872x, and adds the new DTMF example 5. It's not necessary to fix your problem, but that's how I tested it.
Thank you so much. I will get it tested this weekend.
Jon
Rick, One more question before I finish my testing board. Am I am able to use an SD card reader at the same time as the PDM mic? I am assuming not since the SD card reader needs SPI.
Thanks
Jon
SPI only conflicts with PDM on nRF52 if you are using A4, A5. There aren't a lot of spare GPIO on Tracker SoM but if you can find two other pins for PDM that are not used by built-in Monitor One peripherals either you could use them at the same time, but it's really limited by available GPIO.
Hi Rick, Can you share the code you are using. I am still having troubles getting the microphone to work.
I used example 5 but with this initialization code:
SPI.end();
int err = Microphone_PDM::instance()
.withPinCLK(A4)
.withPinDAT(A5)
.withOutputSize(Microphone_PDM::OutputSize::SIGNED_16)
.withRange(Microphone_PDM::Range::RANGE_2048)
.withSampleRate(SAMPLE_FREQ)
.init();
Thank you, I got it working and with the SD card reader.
Rick,
I did get it all to work, but now I want to put it on the proto-type board. Am I able to use A0 and A1 for the PDM CLK and DATA? And if so, do I need to turn something off in my code?
You cannot use A0 and A1 on the Monitor One expansion card for PDM. They're used for I2C for the temperature sensor and RGB LED and those cannot be turned off.