2

I am implementing a oscilloscope GUI to visualize the data coming from the I2S mic connected to ESP32. The data is transmitted to ESP32 over WiFi through TCP socket connection to PC client where the GUI is made.

This is ESP32, server side code.

void setup() {

    const i2s_config_t i2s_config = {
      .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), // Receive, not transfer
      .sample_rate = 16000,                         // 16KHz
      .bits_per_sample = I2S_BITS_PER_SAMPLE_24BIT, // could only get it to work with 32bits
      .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // although the SEL config should be left, it seems to transmit on right
      .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
      .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,     // Interrupt level 1
      .dma_buf_count = 4,                           // number of buffers
      .dma_buf_len = 8                              // 8 samples per buffer (minimum)
    };
};


void loop() {

  WiFiClient client = wifiServer.available();

  if (client) {

    while (client.connected()) {

      while (client.available()>0) {
        int32_t sample = 0;
        int bytes_read = i2s_pop_sample(I2S_PORT, (char *)&sample, portMAX_DELAY); // no timeout
        String samplestring = String(sample*0.00000059);
        client.println(samplestring);
     }

      delay(10);
   }
    client.stop();
    Serial.println("Client disconnected");
   }
}

I am plotting it on a GUI in winform. But when I plot it doesn't give the correct wave format (I tried with 1kHz sine wave). I am loading the data to a textbox and getting converted to double to plot the data.

What is wrong here?

int t;

private void Timer1_Tick(object sender, EventArgs e) { one = new Thread(test); one.Start(); t++; }

public void test() { byte[] bytes = new byte[client.ReceiveBufferSize]; var readCount = stream.Read(bytes, 0, bytes.Length);

string datastring = Encoding.UTF8.GetString(bytes);
txtdata.Invoke((MethodInvoker)(() => txtdata.Text = datastring.Substring(0, 100)));

txtno.Invoke((MethodInvoker)(() => 
    txtno.Text = ("\nnumber of bytes read: " + readCount)
));

String ch1 = txtdata.Text; ;
String[] ch1y = ch1.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);

for (int a = 1; a < ch1y.Length - 1; a++)
{
    chart1.Invoke((MethodInvoker)(() =>
    chart1.Series[0].Points.AddXY(t, Convert.ToDouble(ch1y[a]))
    ));
}

}

I edited the above code like this

private void Timer1_Tick(object sender, EventArgs e)
    {
        byte[] bytes = new byte[client.ReceiveBufferSize];
        var readCount = stream.Read(bytes, 0, bytes.Length);
    string datastring = Encoding.UTF8.GetString(bytes);
    txtdata.Invoke((MethodInvoker)(() =>
                txtdata.Text = datastring.Substring(0, 100)));

    txtno.Invoke((MethodInvoker)(() =>
    txtno.Text = ("\nnumber of bytes read: " + readCount)));

    String ch1 = txtdata.Text; ;
    String[] ch1y = ch1.Split(new char[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
    double[] val = new double[ch1y.Length];

    for (int a = 1; a < ch1y.Length - 1; a++)
    {
        val[a] = double.Parse(ch1y[a]);


        chart1.Series[0].Points.Add(val[a]);
    }
    if (chart1.Series[0].Points.Count > 5)
        chart1.Series[0].Points.RemoveAt(0);
    chart1.ChartAreas[0].AxisX.Minimum = double.NaN;
    chart1.ChartAreas[0].AxisX.Maximum = double.NaN;
}

Bence Kaulics
  • 7,843
  • 8
  • 42
  • 90
Buddhika
  • 29
  • 2

1 Answers1

1

Here are a couple of ideas: I'm with jcaron above that you're having issues with the data split over boundary. Also, the fact that you're sampling at 16kHz for a 1kHz wave means the wave will be pretty jaggy. Try 160Hz for the input. So, here's some pseudo code that may work better: Try it. If it works, you can improve it, etc. It is not memory efficient, but you're running on a PC.

Pseudocode: Have a string that can remember contents between timer ticks. I'll call this incoming_chars. For a quick try, make it global (shudder!). Refine it later.

In your timer tick:

read bytes, convert to string and append that to incoming_chars
while incoming_chars contans '\n'
..get first substring including the '\n' from incoming_chars
..parse that as a double and add to the chart ..cut out that first substring from incoming_chars
end while
if the chart now has too many points, trim old historical ones.

kalyanswaroop
  • 1,208
  • 5
  • 16