Have you noticed how cool products like Alexa are making waves in the maker community? It's all about the magic of microphones! These tiny devices are becoming super popular for creating smart, voice-controlled gadgets that everyone loves. But here's the thing: to make these mic-based gizmos work, you need something called I2S, which is a bus interface especially for microphones. If that sounds too complicated for you, no problem! That's where Micropython swoops in to save the day! Our new enablement makes using I2S a piece of cake, so you can dive into creating awesome microphone-powered inventions without getting lost in technical tangles.
What is I2S?I2S, short for "Inter-IC Sound, " is a serial bus interface that is primarily used to transmit digital audio data. It is commonly found in audio codecs, digital signal processors, and microcontrollers that interface with audio input and output devices. The I2S bus consists of three main connections:
- Serial Clock (SCK): This signal provides the clock for the data transmission. It synchronizes the timing of the data being sent and received.
- Word Select (WS): Also known as "frame sync" or "LRCLK, " this signal indicates the beginning of a new audio frame and specifies whether the following data is for the left or right channel.
- Serial Data (SD): This is where the actual digital audio data is transmitted. It carries the audio samples in a serial format.
The picture demonstrates a timing example of I2S and aims to make the distinction between the pins clear.
The I2S protocol is designed to facilitate the transfer of high-fidelity audio data between integrated circuits in a synchronous manner, making it a popular choice for connecting digital audio devices.
Meet Our MicrophoneThe S2GO MEMSMIC IM69D is a high-performance digital MEMS microphone designed for precise audio capture in embedded applications. With its compact form factor and exceptional signal-to-noise ratio, this microphone offers clear and accurate sound acquisition, making it an ideal choice for voice-controlled devices, audio recording, and acoustic sensing projects. Its compatibility with a range of development platforms, including Raspberry Pi and Arduino. For both, you can already find an existing protip by following the links.
PSoC with MicropythonIn this tutorial we are using MicroPython on the PSoC6™ microcontroller. For this we have already a lot of example with ADC, GPIOs, I2C or wifi connection and CO2 sensor. https://www.hackster.io/Infineon_Team/projects. Feel free to check it out!
Hardware SetupIn the hardware setup, only four connections are actually required, all of which are visible in the graphic. These include the power supply (3.3V) and the three I2S signals, which are as follows:
- CLK → 5.4 WS_PIN
- BCLK → 5.5 SCK_PIN
- DATA → 5.6 SD_PIN
Be carfull don't get confused with the different naming.
A ground conection between both is not necessary and not recommended because you get a more noisy output with that.
SoftwareLet's dive into the fun part! We're making use of MicroPython, and guess what? Setting it up is pretty easy and there's already a step-by-step guide waiting for you. And when it comes to programming, we're keeping it simple and awesome with the Thonny IDE. You can grab it by following the link and start coding away in no time!
Sometimes, even we find inspiration in the work of others and change it for us. The original source of our code can be found here. A special thanks to them for their inspiration!
Time to explore the code! As the foundation for our example, we used a code that you can find here.
import os
from machine import Pin, I2S,
from machine import AUDIO_I2S_98_MHZ, freq
In the snippet above, we're importing the "os" module for operating system interactions. From the "machine" library, we're importing "Pin" for GPIO control, "I2S" for digital audio communication, "AUDIO_I2S_98_MHZ" for setting the I2S clock frequency to 98 MHz, and "freq" for frequency management or clock control.
if os.uname().machine.count("CY8CPROTO-062-4343W"):
# ======= I2S CONFIGURATION =======
SCK_PIN = "P5_4"
WS_PIN = "P5_5"
SD_PIN = "P5_6"
I2S_ID = 0
BUFFER_LENGTH_IN_BYTES = 40000
# ======= I2S CONFIGURATION =======
else:
print("Warning: program not tested with this board")
Next, the code checks the machine type and configures the I2S protocol with the pin assignments we used in the hardware setup.
# ======= AUDIO CONFIGURATION =======
WAV_FILE = "mic.wav"
RECORD_TIME_IN_SECONDS = 10
WAV_SAMPLE_SIZE_IN_BITS = 32
FORMAT = I2S.STEREO
SAMPLE_RATE_IN_HZ = 8000
The code snippet above sets up the audio configuration parameters for recording. It defines the file name as "mic.wav" for the recorded audio, specifies a recording time of 10 seconds, a sample size of 32 bits, a stereo audio format, and a sample rate of 8000 Hz.
format_to_channels = {I2S.MONO: 1, I2S.STEREO: 2}
NUM_CHANNELS = format_to_channels[FORMAT]
WAV_SAMPLE_SIZE_IN_BYTES = WAV_SAMPLE_SIZE_IN_BITS // 8
RECORDING_SIZE_IN_BYTES = (
RECORD_TIME_IN_SECONDS * SAMPLE_RATE_IN_HZ * WAV_SAMPLE_SIZE_IN_BYTES * NUM_CHANNELS
)
Furthermore it creates a mapping of I2S audio formats to the number of channels and calculates the size in bytes required for recording the audio data based on the specified parameters.
# Set the I2S clock frequency
freq(AUDIO_I2S_98_MHZ) # Set clock frequency to 98 MHz for 8kHz sample rate
This line of code sets the I2S clock frequency to 98 MHz, which is suitable for an 8kHz sample rate in digital audio applications. More info you can find here.
def create_wav_header(sampleRate, bitsPerSample, num_channels, num_samples):
datasize = num_samples * num_channels * bitsPerSample // 8
o = bytes("RIFF", "ascii") # (4byte) Marks file as RIFF
o += (datasize + 36).to_bytes(
4, "little"
) # (4byte) File size in bytes excluding this and RIFF marker
o += bytes("WAVE", "ascii") # (4byte) File type
o += bytes("fmt ", "ascii") # (4byte) Format Chunk Marker
o += (16).to_bytes(4, "little") # (4byte) Length of above format data
o += (1).to_bytes(2, "little") # (2byte) Format type (1 - PCM)
o += (num_channels).to_bytes(2, "little") # (2byte)
o += (sampleRate).to_bytes(4, "little") # (4byte)
o += (sampleRate * num_channels * bitsPerSample // 8).to_bytes(4, "little") # (4byte)
o += (num_channels * bitsPerSample // 8).to_bytes(2, "little") # (2byte)
o += (bitsPerSample).to_bytes(2, "little") # (2byte)
o += bytes("data", "ascii") # (4byte) Data Chunk Marker
o += (datasize).to_bytes(4, "little") # (4byte) Data size in bytes
return o
wav = open("/{}".format(WAV_FILE), "wb")
# create header for WAV file and write to SD card
wav_header = create_wav_header(
SAMPLE_RATE_IN_HZ,
WAV_SAMPLE_SIZE_IN_BITS,
NUM_CHANNELS,
SAMPLE_RATE_IN_HZ * RECORD_TIME_IN_SECONDS,
)
num_bytes_written = wav.write(wav_header)
The function "create_wav_header, " is responsible for generating the header for a WAV audio file. It calculates the data size, as well as other parameters such as file size, file type, format type, number of channels, sample rate, and data size.
The function then opens the WAV file, creates the WAV header using the specified audio configuration parameters, and writes the header to the file. The number of bytes written to the file is recorded. This process ensures that the WAV file is formatted correctly with the necessary header information to support the specified audio recording settings.
audio_in = I2S(
I2S_ID,
sck=SCK_PIN,
ws=WS_PIN,
sd=SD_PIN,
mode=I2S.RX,
bits=WAV_SAMPLE_SIZE_IN_BITS,
format=FORMAT,
rate=SAMPLE_RATE_IN_HZ,
ibuf=BUFFER_LENGTH_IN_BYTES,
)
The code initializes an I2S (Inter-IC Sound) instance for audio input with specific parameters such as pin assignments, mode, sample size, format, sample rate, and input buffer length, preparing the interface to receive audio data based on the defined settings from before.
# allocate sample arrays
# memoryview used to reduce heap allocation in while loop
mic_samples = bytearray(10000)
mic_samples_mv = memoryview(mic_samples)
num_sample_bytes_written_to_wav = 0
The code segment allocates memory for storing audio samples using a byte array and memoryview, while also initializing a variable to track the number of audio sample bytes written to the WAV file.
print("Recording size: {} bytes".format(RECORDING_SIZE_IN_BYTES))
print("========== START RECORDING ==========")
try:
while num_sample_bytes_written_to_wav < RECORDING_SIZE_IN_BYTES:
# read a block of samples from the I2S microphone
num_bytes_read_from_mic = audio_in.readinto(mic_samples_mv)
if num_bytes_read_from_mic > 0:
num_bytes_to_write = min(
num_bytes_read_from_mic, RECORDING_SIZE_IN_BYTES - num_sample_bytes_written_to_wav
)
# write samples to WAV file
num_bytes_written = wav.write(mic_samples_mv[:num_bytes_to_write])
num_sample_bytes_written_to_wav += num_bytes_written
print("========== DONE RECORDING ==========")
except (KeyboardInterrupt, Exception) as e:
print("caught exception {} {}".format(type(e).__name__, e))
Finally we start recording! After sending the starting message we are ready to read the data from I2S with the command audio_in.readinto().
After that we write the data into the WAV file until the specified recording size is reached. The code also includes exception handling to catch any interruptions or errors during the recording process, conveying a message indicating the completion of recording or any encountered exceptions. To stop the person from talking we send a "Done Recording" message ;)
# cleanup
wav.close()
audio_in.deinit()
In the end we finalize the recording by closing the WAV file and deactivating the audio input interface.
After the completion of the code you can not run it like normally, it needs to be saved on the PSoC. For this you need to be connected to youre board already. Now, you can select the option "File" -> "Save As" and there will open a window. Here, you will have the option to choose the Micropython device to save the code on the PSoC. If you do so you will find it listed in your MicroPython device on the lower left side of youre IDE.
With the code ready and saved, you're now all set to run it. Simply open the command line and enter the following two commands to execute the code:
import builtins
builtins.execfile("/i2s_recording.py")
Youre output should now look like this. Between both recording lines you have exactly 10 seconds time.
Now that the code has been successfully executed, the "mic.wav" file is safely stored on the MicroPython device. You are able to download it to your computer and play around with it, or give it a listen. The possibilities are endless!
To enhance the audio outcome, consider experimenting with different sampling rates, adjusting the number of channels, or implementing digital signal processing techniques. Additionally, exploring advanced audio processing algorithms, such as noise reduction or signal filtering, can significantly improve the quality of the recorded audio. Don't hesitate to dive into the world of audio engineering and unleash the full potential of your recordings.
Comments