Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP32 I2S 24 bit data reading from INMP441 Mic and Frequency Mirroring #9663

Closed
1 task done
raprakashvi opened this issue May 21, 2024 · 7 comments
Closed
1 task done
Labels
Area: Peripherals API Relates to peripheral's APIs. Type: For reference Common questions & problems

Comments

@raprakashvi
Copy link

Board

ESP WROOM 32

Device Description

Regular ESP32 development board

Hardware Configuration

#define I2S_WS 22
#define I2S_SD 12
#define I2S_SCK 21
#define I2S_PORT I2S_NUM_0
#define BUFFER_LEN 1024

I2S

Version

v2.0.9

IDE Name

Arduino

Operating System

Windows 10

Flash frequency

80 Mhz

PSRAM enabled

no

Upload speed

921600

Description

Hi all,

I am working on a project to capture environmental noise and through the help of community have made decent progress. Currently, I am able to capture acceptable quality of sound using INMP441 mic. The problem comes when I plot the FFT. I am observing frequency mirroring along max_freq/2 , in my case 4000 Hz as my max frequency is 8000 Hz.

As of now , I am reading data in 16 bit format, even though the microphone datasheet says it is 24 bit. In 16 bit I am able to record sound but the issue persists. My question is, how can I modify my code to accept 24 bit data?

Below are two suggestions 1,2, which boils down to recording data as 32 bit, and then dropping the pad byte (or last byte). Would very much appreciate any help here. I have exhausted my limits and still getting 0 value when I try 32 bit or no value.

I was able to fix presence of noise at 1/3rd max frequency by downgrading Arduino Core to 2.0.9 from 2.0.16 as mentioned in another issue but to fix the frequency, apparently I need to figure out the 24 bit part which I am unable to do and reaching out after many tests.

Sketch

#include <WiFi.h>
#include <driver/i2s.h>

#define I2S_WS 22
#define I2S_SD 12
#define I2S_SCK 21
#define I2S_PORT I2S_NUM_0
#define BUFFER_LEN 512

const char* ssid = "Test";  // Replace with your SSID
const char* password = "Pass";  // Replace with your WiFi password
const int serverPort = 12345;

WiFiServer server(serverPort);
int16_t sBuffer[BUFFER_LEN];

void setup() {
  Serial.begin(921600);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  
  Serial.println("Connected to WiFi");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());  // Print the IP address to the serial monitor
  server.begin();

  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 16000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, 
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = 0,
    .dma_buf_count = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = -1,
    .data_in_num = I2S_SD
  };

  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
}

void loop() {
  WiFiClient client = server.available();

  if (client) {
    client.println("ready");
    while (client.connected()) {
      size_t bytes_read;
      i2s_read(I2S_PORT, &sBuffer, sizeof(sBuffer), &bytes_read, portMAX_DELAY);
      client.write((const char *)sBuffer, bytes_read);
    }
  }
}

Debug Message

None

Other Steps to Reproduce

Once you run this code, you can use a python script to read the data and reproduce it:

import socket
import numpy as np
import matplotlib.pyplot as plt
import time
from scipy.signal import spectrogram
from scipy.io import wavfile

# Configuration
SERVER_IP = 'ESP IP add'  # Replace with the ESP32's IP address
SERVER_PORT = 12345
BUFFER_LEN = 512  # Same as the BUFFER_LEN in Arduino code
SAMPLE_RATE = 16000  # Same as the sample_rate in Arduino code
DURATION = 5  # Duration to record in seconds

def wait_for_ready_signal(sock):
    while True:
        data = sock.recv(1024).decode('utf-8')
        if 'ready' in data:
            print("ESP32 is ready")
            break

# Initialize TCP client connection
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((SERVER_IP, SERVER_PORT))

# Wait for the "ready" message from ESP32
wait_for_ready_signal(sock)

def read_audio_data(duration=DURATION,fname = "audio.wav"):
    num_samples = SAMPLE_RATE * duration
    audio_data = np.zeros(num_samples, dtype=np.int16)
    start_time = time.time()
    sample_idx = 0

    while time.time() - start_time < duration:
        data = sock.recv(BUFFER_LEN * 2)  # Receive binary data
        if data:
            samples = np.frombuffer(data, dtype=np.int16)  # Convert to numpy array
            end_idx = sample_idx + len(samples)
            if end_idx > num_samples:
                end_idx = num_samples
                samples = samples[:end_idx - sample_idx]
            audio_data[sample_idx:end_idx] = samples
            sample_idx = end_idx

    # save the audio file as wave file
    
    # sock.close()
    wavfile.write(fname, SAMPLE_RATE, audio_data)

    return audio_data

def plot_audio_data(audio_data):
    time_axis = np.linspace(0, len(audio_data) / SAMPLE_RATE, num=len(audio_data))

    # Plot time series data
    plt.figure(figsize=(15, 5))
    plt.subplot(1, 2, 1)
    plt.plot(time_axis, audio_data)
    plt.title('Time Series Data')
    plt.xlabel('Time [s]')
    plt.ylabel('Amplitude')
    
    # Plot spectrogram
    plt.subplot(1, 2, 2)
    f, t, Sxx = spectrogram(audio_data, SAMPLE_RATE)
    plt.pcolormesh(t, f, 10 * np.log10(Sxx), shading='gouraud')
    plt.title('Spectrogram')
    plt.ylabel('Frequency [Hz]')
    plt.xlabel('Time [s]')
    plt.colorbar(label='Intensity [dB]')
    
    plt.tight_layout()
    plt.show()

 
# Read and plot audio data
i = 1
audio_data = read_audio_data(duration=DURATION,fname=f"audio_{i}.wav")
sock.close()

plot_audio_data(audio_data)

,

For this case, the spectrogram generated will be mirrored at 4000 Hz.

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@raprakashvi raprakashvi added the Status: Awaiting triage Issue is waiting for triage label May 21, 2024
@raprakashvi
Copy link
Author

@PilnyTomas

@P-R-O-C-H-Y
Copy link
Member

Hi @raprakashvi, you may try the latest version 3.0.0-rc3 to use the new I2S library. There should be possible to setup I2S in 24 bit mode. Take a look at the examples so you can simply implement it.

@VojtechBartoska VojtechBartoska added Area: Peripherals API Relates to peripheral's APIs. Resolution: Awaiting response Waiting for response of author and removed Status: Awaiting triage Issue is waiting for triage labels May 22, 2024
@raprakashvi
Copy link
Author

Hi @P-R-O-C-H-Y , thanks for sharing the link. Which library manager is the newer I2S library added to? I had to downgrade my Arduino core to 2.0.9 from 2.0.16 to remove noise at 1/3rd of the max frequency as discussed in another open issue.

Meanwhile, is it possible to suggest improvements in the current code as for transferring data over wifi to a Python script would convert 24 bit to 16 bit again from what I have seen? Thanks.
-rp

@VojtechBartoska
Copy link
Collaborator

@raprakashvi new I2S library is included in 3.0.0 version which is now only in development release stage so you need to update your json link in Arduino IDE. Stable version of 3.0.0 is planned to be released next week.

@P-R-O-C-H-Y
Copy link
Member

@raprakashvi The 3.0.0 stable have been release on Monday. Feel free to take a look and try the new I2S library.
I will close the issue as solved, but be free to reopen or comment if you find anything not working :)

@raprakashvi
Copy link
Author

raprakashvi commented May 30, 2024

Hi all, posting here if anyone in future is struggling with the same problem. I was able to fix the frequency inversion problem and noise issue at 1/3rd of max frequency by updating to latest Arduino core 3.0, and reading the data as 32 bit. But if I tried to send it over wifi and read it on python client side, it did not work, so I had to use the RX transformation of ESP_I2S library to transform to 16 bit and then send it over. Below is my code for future reference.

#include <WiFi.h>
#include "ESP_I2S.h"

#define I2S_WS 22
#define I2S_SD 12
#define I2S_SCK 21
#define BUFFER_LEN 512
static I2SClass i2s;

const char* ssid = "ADD";  // Replace with your SSID
const char* password = "PASS";  // Replace with your WiFi password
const int serverPort = 12345;

WiFiServer server(serverPort);
int16_t i2sBuffer[BUFFER_LEN];  // Adjusted to 16-bit buffer for new I2S library

void setup() {
  Serial.begin(921600);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  Serial.println("Connected to WiFi");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());  // Print the IP address to the serial monitor
  server.begin();

  Serial.println("Initializing I2S bus...");

  // Set up the pins used for audio input
  i2s.setPins(I2S_SCK, I2S_WS, -1, I2S_SD);

  // Initialize the I2S bus in standard mode with transformation to 16-bit
  if (!i2s.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT)) {
    Serial.println("Failed to initialize I2S bus!");
    return;
  }
  
  // Configure the I2S receiver to transform 32-bit data to 16-bit data
  if (!i2s.configureRX(16000, I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO, I2S_RX_TRANSFORM_32_TO_16)) {
    Serial.println("Failed to configure I2S RX transformation!");
    return;
  }

  Serial.println("I2S bus initialized and configured.");
}

void loop() {
  WiFiClient client = server.available();

  if (client) {
    client.println("ready");
    while (client.connected()) {
      size_t bytes_read = i2s.readBytes((char *)i2sBuffer, sizeof(i2sBuffer));
      if (bytes_read > 0) {
        // Send the 16-bit data over WiFi
        client.write((const char *)i2sBuffer, bytes_read);
      } else {
        Serial.println("No bytes read, trying again...");
        delay(100);  // Small delay to avoid flooding the Serial Monitor
      }
    }
  }
}

@me-no-dev
Copy link
Member

That is the exact reason why we added the transform methods :)

@P-R-O-C-H-Y P-R-O-C-H-Y added Type: For reference Common questions & problems and removed Resolution: Awaiting response Waiting for response of author labels May 30, 2024
@me-no-dev me-no-dev changed the title ESP32 I2S 24 bit data reading + INMP441 Mic + Frequency Mirroring ESP32 I2S 24 bit data reading from INMP441 Mic and Frequency Mirroring Jun 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Peripherals API Relates to peripheral's APIs. Type: For reference Common questions & problems
Projects
None yet
Development

No branches or pull requests

4 participants