Skip to content

Commit 1629e98

Browse files
committed
pdm: first version of PDM library supporting Nano 33 BLE board
1 parent e850083 commit 1629e98

File tree

11 files changed

+830
-0
lines changed

11 files changed

+830
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Nano 33 BLE Microphone to Serial Plotter
3+
*/
4+
#include <PDM.h>
5+
6+
#ifndef CONFIG_BOARD_ARDUINO_NANO_33_BLE
7+
#error "Only Nano 33 BLE board is currently supported by this library"
8+
#endif
9+
// default number of output channels
10+
// Nano 33 BLE only supports 1 channel
11+
static const char channels = 1;
12+
// default PCM output frequency
13+
static const int frequency = 16000;
14+
// Buffer to read samples into
15+
// For better performance set the user buffer dimension to the dimension
16+
// of the buffer used by PDM library this way
17+
short sampleBuffer[DEFAULT_PDM_BUFFER_SIZE];
18+
// Number of bytes read
19+
volatile int samplesRead;
20+
21+
void setup() {
22+
Serial.begin(115200);
23+
while (!Serial)
24+
;
25+
26+
// Configure the data receive callback
27+
PDM.onReceive(onPDMdata);
28+
29+
// Initialize PDM:
30+
// - one channel (Mono)
31+
// - 16 kHz sample rate (Standard for voice)
32+
if (!PDM.begin(channels, frequency)) {
33+
Serial.println("Failed to start PDM!");
34+
while (1)
35+
;
36+
}
37+
}
38+
39+
void loop() {
40+
// Wait for samples to be read
41+
if (samplesRead) {
42+
43+
// Print samples to the serial monitor or plotter
44+
for (int i = 0; i < samplesRead; i++) {
45+
if (channels == 2) {
46+
Serial.print("L:");
47+
Serial.print(sampleBuffer[i]);
48+
Serial.print(" R:");
49+
i++;
50+
}
51+
Serial.println(sampleBuffer[i]);
52+
}
53+
54+
// Clear the read count
55+
samplesRead = 0;
56+
}
57+
}
58+
59+
// Callback function: Handling the PDM on receive event
60+
// The PDM library will call this callback function as soon as the internal
61+
// buffer is ready and can be read
62+
// It is user responsibility to read from PDM as fast as possible otherwise
63+
// data will be lost
64+
void onPDMdata() {
65+
// Query the number of bytes available
66+
int bytesAvailable = PDM.available();
67+
// Read into the sample buffer
68+
PDM.read(sampleBuffer, bytesAvailable);
69+
70+
// 16-bit, 2 bytes per sample
71+
samplesRead = bytesAvailable / 2;
72+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import serial
2+
import wave
3+
import struct
4+
import time
5+
6+
# --- CONFIGURATION ---
7+
SERIAL_PORT = '/dev/ttyACM0' # CHECK YOUR ARDUINO IDE FOR THE CORRECT PORT!
8+
BAUD_RATE = 115200
9+
OUTPUT_FILENAME = "recording.wav"
10+
RECORD_SECONDS = 5 # How long to record
11+
SAMPLE_RATE = 16000 # Must match Arduino sketch
12+
# ---------------------
13+
14+
def record_audio():
15+
try:
16+
ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
17+
print(f"Connected to {SERIAL_PORT}. Recording for {RECORD_SECONDS} seconds...")
18+
19+
# Clear buffer to avoid old data
20+
ser.reset_input_buffer()
21+
22+
audio_frames = []
23+
start_time = time.time()
24+
25+
# Calculate how many bytes we expect
26+
# 16000 samples/sec * 2 bytes/sample * seconds
27+
total_bytes = SAMPLE_RATE * 2 * RECORD_SECONDS
28+
bytes_received = 0
29+
30+
while bytes_received < total_bytes:
31+
if ser.in_waiting > 0:
32+
# Read whatever is available
33+
data = ser.read(ser.in_waiting)
34+
audio_frames.append(data)
35+
bytes_received += len(data)
36+
37+
# Simple progress indicator
38+
print(f"Recorded: {bytes_received} / {total_bytes} bytes", end='\r')
39+
40+
print("\nRecording complete. Saving...")
41+
ser.close()
42+
43+
# Save to WAV file
44+
with wave.open(OUTPUT_FILENAME, 'wb') as wf:
45+
wf.setnchannels(1) # Mono
46+
wf.setsampwidth(2) # 16-bit (2 bytes)
47+
wf.setframerate(SAMPLE_RATE)
48+
wf.writeframes(b''.join(audio_frames))
49+
50+
print(f"File saved: {OUTPUT_FILENAME}")
51+
52+
except serial.SerialException:
53+
print(f"Error: Could not open port {SERIAL_PORT}. Is the Arduino Monitor open? Close it!")
54+
except Exception as e:
55+
print(f"An error occurred: {e}")
56+
57+
if __name__ == "__main__":
58+
record_audio()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
Nano 33 BLE Microphone to Serial Streamer
3+
4+
How to use this example
5+
-----------------------
6+
7+
Download this sketch into the Nano33 BLE
8+
This will start immediately mic acquisition
9+
Be sure that serial monitor or any other program is not accessing the Nano33
10+
serial port
11+
Start the python script present in the same folder (getWawe.py)
12+
This python script will get the data streamed by this sketch in the
13+
serial port and pack them into a wav file you can listen with any player on
14+
your PC
15+
NOTE:
16+
the python script uses /dev/ttyACM0 as default serial device, change it
17+
accordingly if the nano33 serial does not correspond to this device
18+
NOTE: the python script only records about 5 seconds of sounds
19+
*/
20+
#include <PDM.h>
21+
22+
#ifndef CONFIG_BOARD_ARDUINO_NANO_33_BLE
23+
#error "Only Nano 33 BLE board is currently supported by this library"
24+
#endif
25+
26+
// default number of output channels
27+
static const char channels = 1;
28+
// default PCM output frequency
29+
static const int frequency = 16000;
30+
// Buffer to read samples into
31+
// For better performance set the user buffer dimension to the dimension
32+
// of the buffer used by PDM library this way
33+
short sampleBuffer[DEFAULT_PDM_BUFFER_SIZE];
34+
// Number of bytes read
35+
volatile int samplesRead;
36+
37+
void setup() {
38+
Serial.begin(115200);
39+
while (!Serial)
40+
;
41+
42+
// Configure the data receive callback
43+
PDM.onReceive(onPDMdata);
44+
45+
// Initialize PDM:
46+
// - one channel (Mono)
47+
// - 16 kHz sample rate (Standard for voice)
48+
if (!PDM.begin(channels, frequency)) {
49+
Serial.println("Failed to start PDM!");
50+
while (1)
51+
;
52+
}
53+
}
54+
55+
void loop() {
56+
// Wait for samples to be read
57+
if (samplesRead) {
58+
59+
// Write the raw bytes to the Serial port
60+
// We send byte-by-byte to keep it fast and binary
61+
Serial.write((uint8_t *)sampleBuffer, samplesRead * 2);
62+
// Clear the read count
63+
samplesRead = 0;
64+
}
65+
}
66+
67+
// Callback function: Handling the PDM on receive event
68+
// The PDM library will call this callback function as soon as the internal
69+
// buffer is ready and can be read
70+
// It is user responsibility to read from PDM as fast as possible otherwise
71+
// data will be lost
72+
void onPDMdata() {
73+
// Query the number of bytes available
74+
int bytesAvailable = PDM.available();
75+
// Read into the sample buffer
76+
PDM.read(sampleBuffer, bytesAvailable);
77+
78+
// 16-bit, 2 bytes per sample
79+
samplesRead = bytesAvailable / 2;
80+
}

libraries/PDM/keywords.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#######################################
2+
# Syntax Coloring Map PDM
3+
#######################################
4+
5+
#######################################
6+
# Datatypes (KEYWORD1)
7+
#######################################
8+
9+
PDM KEYWORD1
10+
11+
#######################################
12+
# Methods and Functions (KEYWORD2)
13+
#######################################
14+
begin KEYWORD2
15+
end KEYWORD2
16+
17+
available KEYWORD2
18+
read KEYWORD2
19+
20+
onReceive KEYWORD2
21+
22+
setGain KEYWORD2
23+
setBufferSize KEYWORD2
24+
25+
#######################################
26+
# Constants (LITERAL1)
27+
#######################################

libraries/PDM/library.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=PDM
2+
version=1.0
3+
author=Arduino
4+
maintainer=Arduino <info@arduino.cc>
5+
sentence=Enables the communication with devices that use the PDM Bus
6+
paragraph=
7+
category=Communication
8+
url=
9+
architectures=zephyr_main,zephyr_contrib
10+
includes=PDM.h

0 commit comments

Comments
 (0)