Page 1 of 1

External ADC Question

Posted: Wed Nov 06, 2019 1:24 pm
by zekageri
Is anybody used an external adc with esp32?
I want to use the MCP33131D SPI adc.
https://www.microchip.com/wwwproducts/en/MCP33131D-10
Any toughts?

Re: External ADC Question

Posted: Wed Nov 06, 2019 4:47 pm
by idahowalker
How is it better then
The ESP32 integrates two 12-bit SAR (Successive Approximation Register) ADCs supporting a total of 18 measurement channels (analog enabled pins).
https://docs.espressif.com/projects/esp ... s/adc.html
?

Re: External ADC Question

Posted: Thu Nov 07, 2019 12:06 pm
by zekageri
ESP's internal ADC is crap in itself.
I can not measure properly in it.
I was used the adc1 with i2s dma before, and the signal was noisy A.F.
I realized that i need to pull the gpio36 and 39 down because of the noise.
So i lost two pins because of just that.
After that i realized that if i get any interrupt on any of the ADC1 pins, the i2s goes crazy.
So if i want to read analog values with the internal adc i have to use all the channels just for that.
This is not satisfying at all.

Re: External ADC Question

Posted: Thu Nov 07, 2019 4:04 pm
by idahowalker
I get great results from the built in A:D's

and an example of the way I am using the ADC.

Code: Select all

#include <driver/adc.h>

setup()
{
 // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  // set up A:D channels
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
  //  adc1_config_width(ADC_WIDTH_12Bit);
  //  adc1_config_channel_atten(ADC1_CHANNEL_1, ADC_ATTEN_11db);
  /*
     When VDD_A is 3.3V: 11dB attenuation (ADC_ATTEN_11db) gives full-scale voltage 3.3V
  */
}
////
void TaskAnalogVoltRead_LIDAR( void *pvParameters )
{
  // String localBuffer;
  // localBuffer.reserve ( StringBufferSize300 );
  int iBit = 1;
  float ADbits = 4095.0f;
  float offSET = 1.0f;
  float r1 = 100800.0f; // R1 in ohm, 100k = 100,800.0 //
  float r2 = 38780.0f; // R2 in ohm, 38k = 38000.0 //actual 38780K
  float uPvolts = 3.3f;
  // ADC1 channel 0 is GPIO36
  // ADC1 channel 1 is GPIO37
  // ADC1 channel 6 is GPIO34
  // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  // to get resistor R2 go to:
  // http://www.ohmslawcalculator.com/voltage-divider-calculator
  //   used 12 volts for the input voltage to calculate R2, used 100K for R1
  for (;;)
  {
    // group handle, WaitForBits, ClearOnExitBit, WaitForAllBits, TicksToWait
    xEventGroupWaitBits( eg, evtAnalogVoltReadTask_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY );
    //    // read the input
    iBit = iBit << 1;
    //    //    Serial.println ( iBit );
    if ( iBit == 1073741824 )
    {
      if ( xSemaphoreTake( sema_AnalogVoltRead_LIDAR, xSemaphoreTicksToWait ) == pdTRUE )
      {
        ptrVbatt_LIDAR[0] += ( ((( uPvolts * adc1_get_raw(ADC1_CHANNEL_6)) / ADbits) / r2 * ( r1 + r2)) + offSET );
        ptrVbatt_LIDAR[0] = ptrVbatt_LIDAR[0] / 2; // average readings
      } // if ( xSemaphoreTake( sema_AnalogVoltRead_LIDAR, xSemaphoreTicksToWait ) == pdTRUE )
      iBit = 1;
    } // if ( iBit == 1073741824 )
  }
  vTaskDelete( NULL );
}

Re: External ADC Question

Posted: Fri Nov 08, 2019 1:51 am
by knightridar
I just posted some code for ADC readings for the ESP32.
It takes values from an array and smooths them out.
viewtopic.php?f=18&t=13053

I get good results after smoothing out the values and using 0.1 uF capacitors.
See this in Espressif documentation:
https://docs.espressif.com/projects/esp ... s/adc.html

Multisampling and capacitors help a lot.

Idahowalker's advice that I got a while back about using the code in post I mentioned above is also good versus using analogRead commands of Arduino IDE.

Re: External ADC Question

Posted: Fri Nov 08, 2019 2:17 pm
by idahowalker
Also, I found that by sampling the ADC regularly once a mS or so, and applying the Simple Kalman filter to the results https://github.com/denyssene/SimpleKalmanFilter, the output gets real smooth. Even if the A:D converter values are not displayed regularly, like once a second, getting several readings per second and apply the simple Kalman with each sampling does a real good job.

The simple Kalman results are better with an accurate time between iterations.

Here

Code: Select all

SimpleKalmanFilter X_KF( (float)iX_Posit90, (float)iX_Posit90, float(ServoDelay12mS) / 1000.0f );
I use the time of task iteration.

Here I use the 64 bit ESP32 uS clock to get time iterations.

Code: Select all

void fDo_AudioReadFreq( void *pvParameters )
{
  int64_t EndTime = esp_timer_get_time();
  int64_t StartTime = esp_timer_get_time(); //gets time in uSeconds like Arduino Micros
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDo_AudioReadFreq, pdTRUE, pdTRUE, portMAX_DELAY);
    EndTime = esp_timer_get_time() - StartTime;
// the other code stuff thing do's
// and more code thing do's
// and even more code thing do's
    StartTime = esp_timer_get_time();
    // }
  }
  vTaskDelete( NULL );
} // fDo_ AudioReadFreq( void *pvParameters )

Re: External ADC Question

Posted: Mon Nov 11, 2019 9:50 am
by zekageri
I was using the adc with i2s dma. My code looks like this:

I2S config:

Code: Select all

void configure_i2s(){
  //pinMode(36,INPUT);
  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 = 144444,                                             // sample rate
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,                                 // 16 bit I2S
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, //I2S_CHANNEL_FMT_ALL_LEFT,                                   // only 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
    .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(3000/portTICK_RATE_MS);
  }
Read from DMA BUFFER and plot the results to web:

Code: Select all

static const inline void Plot_DMA_Buffer(){
  int Vmax = 0;
  int Vmin = 4095;
  uint16_t Buffer[NUM_SAMPLES];
  for(int i = 0;i<NUM_SAMPLES;i++){
    Buffer[i] = i2s_read_buff[i];
  if(i != NUM_SAMPLES)
  {
    if((i2s_read_buff[i]<i2s_read_buff[i+1])&&i2s_read_buff[i]<Vmin){
      if(constrain(i2s_read_buff[i],Vmin-300,Vmin+300)){
        Vmin = i2s_read_buff[i];
      }
    }else if((i2s_read_buff[i]>i2s_read_buff[i+1])&&i2s_read_buff[i]>Vmax){
      if(constrain(i2s_read_buff[i],Vmax-300,Vmax+300)){
        Vmax = i2s_read_buff[i];
      }
    }
  }
  }
  int Vamp = (Vmax) - (Vmin);
  if(millis() - Start_Millis >= 1000){
    Start_Millis = millis();
    Send_Amplitude(Vamp);
    if(olvasunk_e){
      for(int k = 0;k<NUM_SAMPLES;k++){
        if(!olvasunk_e){
          break;
        }else{
          String data = (String)Buffer[k]+"}";
          webSocket.broadcastTXT(data.c_str(), data.length());
        }
      }
    }
  }
}

Code: Select all

static const inline void Base_Setups_Core1(){
    Serial.begin(115200);
    SPIFFS.begin() ? Serial.println("SPIFFS.OK") : Serial.println("SPIFFS.FAIL");
    configure_i2s();

    /** Ethernet wifi stack **/
    WiFi.onEvent(WiFiEvent);
    ETH_begin();
    ethernet.config(ip1, nm1, gw1);
    TCP_Requests();
    server.begin();
    ws.onEvent(onWsEvent);
    server.addHandler(&ws);
    External_Interrupts();
    xTaskCreatePinnedToCore ( loop0, "v_getIMU0", 1024, NULL, 0, &TaskHandle_1, 1 );
    xTaskCreatePinnedToCore ( loop1, "v_getIMU1", 15048, NULL, 0, &TaskHandle_2, 0 );
}

static const inline void Main_Loop(){
  if(!Ota_is_Started){
     Plot_DMA_Buffer();                            // RETRIVE DATA FROM i2s_read_buff FOR CALCULATION AND VISUALIZATION / COMPUTE THE AVERAGE AMPLITUDE
  }
}

static void loop0(void * pvParameters){
  for( ;; ){    
        i2s_read(I2S_NUM_0, (char*)i2s_read_buff,NUM_SAMPLES * sizeof(uint16_t), &bytes_read, portMAX_DELAY);
  }
}
The problem with this is that i can not do anything on that core. And the results are noisy.
I don't know how the task semaphores or the task events are working. :(

Re: External ADC Question

Posted: Wed Jan 15, 2020 12:06 pm
by jgustavoam
Hi,
If you are interested, take a look at my project.

ESP32 Digital Voltmeter
https://www.elektormagazine.com/labs/es ... -voltmeter
https://github.com/Gustavomurta/ESP32-DVM

ESP32 - Digital Voltmeter using old style ADC conversion method. High precision, good stability , negative and positive Voltages !
Range -2V to +2V . Method of Voltage measurement - Dual slope integrating ADC conversion. Sample rate = 12 samples/sec.

TLC7135 is a high precision monolithic 4 1/2 digit A/D converter. Dual slope conversion reliability
is combined with ±1 in 20,000 count accuracy and a 2.0000V full scale capability. It features high impedance differential inputs, nearly ideal differential linearity, true ratiometric operation, auto zero and auto-polarity.