ESP32 I2S - Need help understanding how it works

Edit: this sketch uses version 2.0.16 from espressif-arduino framework

I am trying to learn how to use I2S but I am not getting the output I expect.

I am feeding at 2.5KHz pwm signal with a 50% duty cycle into the I2S input pin.

I2S sampling frequency is 10KHz so I think that means each
period of the pwm gets sampled 4 times. Not sure if that is correct or not.

I print out some of the stored samples and they are not what I expect so I am
thinking I don't understand how it works.

Using the ADC_DAC_MODE mode the library sets up the sample resolution to 16 bits
so I would expect stored sample values to be either 0 or 4095.

When I print out some of the samples I get what is below. I am expected 2 values of
zero and two values of 4095. But I am getting 8 values of non-zero and 8 values of zero.

I did confirm the pwm is working and goes from 0 to 3.3 volts

Maybe someone can point me in the right direction?
The code is shown below .

0
0
0
0
0
0
0
0
0
0
799
799
762
762
731
731
699
699
0
0
0
0
0
0
0
0
727
727
693
693
659
659
630
630
0
0
0
0
0
0
0
0
727
727
690
690
658
658
628
628
0
0
0
0
0
0
0
0
728
728
694
694
660
660
629
629

#include <Arduino.h>
#include <I2S.h>

// ADC DEFINES
#define ADC_INPUT_PIN (32)
#define I2S_SAMPLE_RATE (10000) // REAL SAMPLING RATE = I2S_SAMPLE_RATE/((BIT_DEPTH/8) * NUM_CHANNELS) = 40,000/((16/8)* 2) = 10khz
#define BIT_DEPTH (16)

// LEDC DEFINES
#define LEDC_DUTY_CYCLE (50)
#define LEDC_CHAN_0 (0)
#define LEDC_OUT_PIN (27)
#define LEDC_RESOLUTION_8 (8)
// #define LEDC_FREQUENCY (I2S_SAMPLE_RATE / (4 * 4)) // 4 SAMPLES PER PERIOD, (BIT_DEPTH/8)*NUM_CHANNELS
#define LEDC_FREQUENCY (I2S_SAMPLE_RATE / 4) // 4 SAMPLES PER PERIOD, (BIT_DEPTH/8)*NUM_CHANNELS

// bytes= 2 channels * 2 bytes/channel * 4 samples = 16 bytes will be needed to store data for 1 period.

void setup()
{
  char msg[50] = "";
  Serial.begin(115200);

  // PWM setup
  // Serial.printf("Setting up PWM: frequency = %d; resolution bits %d; Duty cycle = %d; duty value = %d, Output pin = %d\n", PWM_FREQUENCY, PWM_RESOLUTION_BITS, PWM_DUTY_PERCENT, PWM_DUTY_VALUE, OUTPUT_PIN);
  uint32_t freq = ledcSetup(LEDC_CHAN_0, LEDC_FREQUENCY, LEDC_RESOLUTION_8);
  if (freq != LEDC_FREQUENCY)
  {
    Serial.printf("Error setting up PWM. Halt!");
    while (1)
      ;
  }
  ledcAttachPin(LEDC_OUT_PIN, LEDC_CHAN_0);     // connect ledc chan 0 to output pin
  uint8_t counts = 255 * LEDC_DUTY_CYCLE / 100; // calc counts to get desired duty cycly
  ledcWrite(LEDC_CHAN_0, counts);               // write duty cycle value to the ledc
  delay(10);

  

  for (int i = 0; i < 100; i++)
  {
    int sample = I2S.read();
    Serial.println(sample);
  }
}

void loop()
{
  int i = 0, sample = 0;

  for (i = 0; i < 100; i++)
  {
    int sample = I2S.read();
    Serial.println(sample);
  }
  // }
  // Serial.println((ledcRead(0) * 100) / 255);

  while (1)
    ;
}

what is the final purpose of sampling the pwm-signal?

Is it measuring duty-cycle?
is it measung the frequency?
or
does the pwm-signal has some deviations that you want to record and re-produce?
or
is the main purpose understanding how I2S-sampling works ?

As you may know I2S stands for Integrated Inter-IC Sound Bus
It is made for sound.

Of course you can create a pwm-signal and use this for creating sound.
a rectangluar signal sounds "sharp" because there are so many harmonics.

mathematically a rectangular signal is a sum if an infinite number of sine-signals with from a row of frequencies where the amplitude of each sinewave with a higher frequency is smaller than the one before in the row

It is explained here

quoting the most interesting part

The end result of adding the first five odd harmonic waveforms 
together (all at the proper  amplitudes, of course) is a close approximation 
of a square wave. The point in doing this is  to illustrate how 
we can build a square wave up from multiple sine waves at different 
frequencies, to prove that a pure square wave is actually equivalent 
to a series of sine waves.

When a square wave AC voltage is applied to a circuit with reactive components 
(capacitors  and inductors), those components react as if they were being 
exposed to several sine wave voltages of different frequencies, 
which in fact they are.

It might be that this effect does have a reasonable influence on the numbers you get.

So if your final purpose is measuring frequency and or duty-cycle
use interrupts on rising / falling edges of the pwm-signal and capture timestamps or let do one of the hardware-counters inside the ESP32 start/stop on rising/falling edges of the signal

EDIT: I think the duplicate values is because I2s samples one channel then the other. So that means the pwm signal is getting sampled 4 times when high and 4 times when low for each channel. Which is twice what I would expect.

I am just using the pwm signal because it is easy to use as a source to sample.

Main thing here is just learning how to use the I2S.

I was expecting it to sample the wave form 4 times a period based on sampling frequency and pwm frequency.

Then print out stored values to confirm sampling is correct. So I would expect
4095, 4095,0,0 repeatedly.

Thanks for Fourier analysis review but it has nothing to do with what I'm trying to do.

Are you doing anything to synchronize the I2S clock and data??? Just feeding some random PWM signal to the I2S data pin is not going to give you anything of value. And I can't see the reason to take this approach. The I2S protocol is about as simple as it gets, with VERY clearly defined clock and data timing. So, I can't fathom what it is you're trying to learn by taking this very odd approach.

That is a remarkably bad idea. I2S clock frequencies are typically 1-2 MHz. And why "pwm"?

Read up on the theory, start with one of the standard I2S libraries, and learn what goes on under the hood.

Hey, thanks for all the helpful responses. I don’t know what I’d do without you guys.