[SOLVED] ESP32 Arduino Framework. Proper I2S ADC DMA reading and Plotting question.

zekageri
Posts: 43
Joined: Mon Sep 03, 2018 11:04 am

[SOLVED] ESP32 Arduino Framework. Proper I2S ADC DMA reading and Plotting question.

Postby zekageri » Tue Oct 01, 2019 11:34 am

Hello forumers!

I want to read a 4Khz analog signal from a signal generator via i2s dma with ESP32 on arduino framework.
My basic idea is this:


-Setup i2s DMA to put the reading to a buffer.
-Assign a read function/task to the core0 to read the values from the DMA buffer.
-Assign a "plotting" functon/task to a core to plot the readings to the web with ChartJS.


What have i done so far:

I created the i2s setup function and put it to the first place on the setup().
I created the reader task on the core0 for reading and plotting to the web.
I created a html file that i store on the SPIFFS file system on the ESP32.

-In the HTMl file i created a chart on a canvas using ChartJS javascript library.
-In the JavaScript file i created a websocket client that is connected to the websocket server on the esp32.
-In the JavaScript file, when the data comes from the esp32 i assign it to the chart and visualize it.


Everything is working fine, except one little problem.
My i2s dma buffer is overflowing and i can see it in the visualized data.


What i think is happening:
-While i read the entire DMA buffer, the i2s adc is filling up this buffer again and again even if i doesn't end up reading from the entire buffer. This will result on buffer overflow and my readed data does not mach the actual reading.

What i think is the solution:
-If there will be an interrupt on the i2s adc dma, if it is filled up the entire buffer i would switch to an other buffer while i read the first, and when i done with the reading i would read from the second while the adc filling up the first and so on...

The thing is i did not find anything from this interrupt on the Arduino core. The one thing that i found is an event solution, that i think is just a flag and does not work as expected.

So my question is, how can i avoid this buffer overflow or how can i assign an interrupt to the dma buffer, or how can it be done correctly.

Here is my i2s init code:

Code: Select all

void configure_i2s(){
  i2s_config_t i2s_config = 
    {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),  // I2S receive mode with ADC
    .sample_rate = 9000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,                  // 16 bit I2S
    .channel_format = I2S_CHANNEL_FMT_ALL_LEFT,                  // all the left channel, no other options works for me!
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),   // I2S format
    .intr_alloc_flags = 1,    // Tried buffer interrupt somehow.
    .dma_buf_count = 8,    // number of DMA buffers. ( Tried different options )
    .dma_buf_len = 1000,  // number of samples.  ( Tried different options )
    .use_apll = 0,              // no Audio PLL ( I dont need the adc to be accurate )
  };
  adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_11db);  // ADC_CHANNEL is ADC_CHANNEL_0 - GPIO34.
  adc1_config_width(ADC_WIDTH_12Bit);                                      // I need 0 - 4095.
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);                  // I2S_NUM_0 works for adc.
 
  i2s_set_adc_mode(ADC_UNIT_1, ADC_CHANNEL);
  SET_PERI_REG_MASK(SYSCON_SARADC_CTRL2_REG, SYSCON_SARADC_SAR1_INV);  // Inverse because fifo.
  i2s_adc_enable(I2S_NUM_0);
}
Here is my adc reader function on core 0:

Code: Select all

size_t bytes_read;
static const inline void ADC_Sampling(){
  uint16_t i2s_read_buff[NUM_SAMPLES];
  String data;
  //xQueueReceive(i2s_queue, &event, (2*samplingFrequency)/  portTICK_PERIOD_MS); // Tried some kind of an event thing. Restarts
  i2s_read(I2S_NUM_0, (char*)i2s_read_buff,NUM_SAMPLES * sizeof(uint16_t), &bytes_read, portMAX_DELAY);  // I2S read to buffer
  if(I2S_EVENT_RX_DONE){  // This is the flag that i mentioned. Does nothing if there are no events assigned based on my observation.
  
    //i2s_adc_disable(I2S_NUM_0);  // Some comment war
  //long Millis_Now = millis();
  //if (((Millis_Now - Start_Sending_Millis) >= DUMP_INTERVAL)&& Chart_is_ok_to_Send_Data)
  //{
    //Start_Sending_Millis = Millis_Now;
    
    for (int i=0;i<NUM_SAMPLES;i++) {
      data = String((i2s_read_buff[i])/(float)40.95);  // Actual read from buffer, i need the data in % format on the web.
      if(olvasunk_e){                                                // Want to send String.
      data += "}";
      webSocket.broadcastTXT(data.c_str(), data.length());  // Send the data with webSocket.
      }
    }
    
    //i2s_zero_dma_buffer(I2S_NUM_0);  // Comment war again.
    //i2s_adc_enable(I2S_NUM_0);
  //}
  
  }
}
The ADC_Sampling is looks like this on core0:

Code: Select all

static void loop0(void * pvParameters){
  for( ;; ){
    vTaskDelay(1);          // REQUIRED TO RESET THE WATCH DOG TIMER IF WORKFLOW DOES NOT CONTAIN ANY OTHER DELAY
    ADC_Sampling1();         // RETRIVE DATA FROM DMA BUFFER FOR CALCULATION AND VISUALIZATION
  }
}
My HTML and JavaScript code:

Code: Select all

<!DOCTYPE HTML>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>      
<script type = "text/javascript" >
  /** new chart  **/
var yVal , xVal = 0;
var updateCount = 0;
var dataPoints = [];
var chart;
window.onload = function () {
chart = new CanvasJS.Chart("chartContainer", {
	zoomEnabled: true,
     	rangeChanging: customInterval,
		title : {
			text : "Osc_Test"
		},
		data : [{
				type : "line",
				dataPoints : dataPoints
			}
		],
		
	});
chart.render();
}	

var updateChart = function () {
	updateCount++;
	dataPoints.push({
	y : yVal,
	x : xVal--
});
if (dataPoints.length >  500 )
	  {
		dataPoints.shift();
	}    
chart.options.title.text = "Update " + updateCount;
chart.render();   
};

webSocket1 = new WebSocket('ws://' + window.location.hostname + ':81/');
webSocket1.onmessage=function(a){
var t = a.data;
if(t.indexOf('}')>-1){
			var l = t.substring(0,t.length-1);
    		yVal = parseInt(l,10);
    		updateChart();
	   }
	};
</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.js"></script>
</head>
<style>
.button {
  background-color: #818a8a;
  color: #FFFFFF;
  float: right;
  padding: 10px;
  border-radius: 10px;
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
}

#mydiv {
    position:fixed;
    top: 40%;
    left: 30%;
    margin-top: -9em; /*set to a negative number 1/2 of your height*/
    margin-left: -15em; /*set to a negative number 1/2 of your width*/
}

</style>
<body>
    <div style = "height: 400px; width: 70%;"id="mydiv">
<div id = "chartContainer" style = "height: 400px; width: 70%;"></div>
<div class="button" width="60" height="100"><a href="MeresStop">STOP PLOTTING</a></div>
<div class="button" width="60" height="100"><a href="MeresOk">START PLOTTING</a></div>
</div>
</body>
</html>
Here is a video URL from my plotted data on the WEB when the buffer overflow doesn't hit.:
https://streamable.com/9dngp
I attached some pictures from the readed data on the web.
Attachments
Képernyőfelvétel (23).png
Képernyőfelvétel (23).png (30.67 KiB) Viewed 7483 times
Képernyőfelvétel (22).png
Képernyőfelvétel (22).png (31.2 KiB) Viewed 7483 times
Képernyőfelvétel (21).png
Képernyőfelvétel (21).png (53.88 KiB) Viewed 7483 times

zekageri
Posts: 43
Joined: Mon Sep 03, 2018 11:04 am

Re: [SOLVED] ESP32 Arduino Framework. Proper I2S ADC DMA reading and Plotting question.

Postby zekageri » Wed Oct 09, 2019 7:04 am

My problem is partially solved.
The solution was that i had to separate my i2s reading and plotting function and put them on the two cores.

Now my i2s reading function on core 1 is looks like this:

Code: Select all

static const inline void Sampling(){
    i2s_read(I2S_NUM_0, (char*)i2s_read_buff,NUM_SAMPLES * sizeof(uint16_t), &bytes_read, portMAX_DELAY);
}
And my plotting function on core 0:

Code: Select all

static const inline void Plotting(uint16_t* Buffer){
   String data;
  if (Chart_is_ok_to_Send_Data)  // if the user on the chart page
  {
    for(int i = 0;i<NUM_SAMPLES;i++)
    {
      data = String((Buffer[i])/(float)40.95);  // float to see the data in % on the web
      if(olvasunk_e){                                     // There is a button on the web to stop just the plotting
      data += "}";
      webSocket.broadcastTXT(data.c_str(), data.length());
      }
    }
  }
}
And my i2s init code on the first place in the setup() :

Code: Select all

void configure_i2s(){
  i2s_config_t i2s_config = 
    {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),  // I2S receive mode with ADC
    .sample_rate = samplingFrequency,                                             // 144000
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,                                 // 16 bit I2S
    .channel_format = I2S_CHANNEL_FMT_ALL_LEFT,                                   // all the left channel
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),   // I2S format
    .intr_alloc_flags = 0,                                                        // none
    .dma_buf_count = 2,                                                           // number of DMA buffers 2 for fastness
    .dma_buf_len = 1024,                                                   // number of samples
    .use_apll = 0,                                                                // no Audio PLL
  };
  adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_11db);
  adc1_config_width(ADC_WIDTH_12Bit);
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
 
  i2s_set_adc_mode(ADC_UNIT_1, ADC_CHANNEL);
  SET_PERI_REG_MASK(SYSCON_SARADC_CTRL2_REG, SYSCON_SARADC_SAR1_INV);
  i2s_adc_enable(I2S_NUM_0);
  vTaskDelay(1000);
}
It will be better if i would use queues but i can't find any usefull example about i2s queues on arduino core.
I attached some pictures from the visualized data on the web.
Attachments
Képernyőfelvétel (27).png
Képernyőfelvétel (27).png (73.11 KiB) Viewed 7372 times
Képernyőfelvétel (26).png
Képernyőfelvétel (26).png (78.74 KiB) Viewed 7372 times

Who is online

Users browsing this forum: Google [Bot] and 43 guests