AnalogRead with I2S Sampling while running WIFI server isn't working

Ewach1
Posts: 2
Joined: Thu Nov 18, 2021 8:48 pm

AnalogRead with I2S Sampling while running WIFI server isn't working

Postby Ewach1 » Fri Nov 19, 2021 5:10 pm

Hello,

I am running into a problem that I cannot seem to solve.

My device is acting as a WIFI server with an interactive webpage loaded to SPIFFS.

I am using I2S to perform high speed sampling of a sensor which is then processed for the user.

I did change the WIFI sleep mode, so that it never sleeps and that allowed me to run both WIFI and I2S correctly.

However, I also require low speed occasional sampling of another sensor, and I cannot seem to make that work.

If I try to disable and then enable the I2s bus with I2S_adc_enable and I2s_adc_disable, it crashes with the following error.

assertion "pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle()" failed: file "IDF/components/freertos/queue.c", line 782, function: xQueueGenericSend


If I try to simply sample with analogRead, the program gets stuck there and never returns.

Any advice or insight would be greatly appreciated.

Ewach1
Posts: 2
Joined: Thu Nov 18, 2021 8:48 pm

Re: AnalogRead with I2S Sampling while running WIFI server isn't working

Postby Ewach1 » Sat Nov 20, 2021 3:41 pm

Here is the code running on the ESP32.

The Sample Battery task is the task I am trying to get working.

Let me know if you have any other questions.

Code: Select all

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
//#include "soc/soc.h"
//#include "soc/rtc_cntl_reg.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <driver/adc.h>
#include "esp_adc_cal.h"
#include <ESPmDNS.h>
#include "FS.h"
#include <LITTLEFS.h>
#include <SPIFFS.h>
#include "AsyncTCP.h"
#include <ESPAsyncWebServer.h>
#include <driver/i2s.h>


#define ADC_INPUT ADC1_CHANNEL_0 //pin 36


const double samplingFrequency = 20000;
const int SAMPLEBLOCK = 200;
const int NUM_BUFS = 8;
const int PROC_SIZE = 200;
const int FINAL_SIZE = 1020;
const i2s_port_t I2S_PORT = I2S_NUM_0;
uint16_t i2s_data[PROC_SIZE] = {0}; // I2s read buffer.
uint16_t i2s_data1[PROC_SIZE] = {0};  // Storage/processing buffer 1
uint16_t i2s_data2[PROC_SIZE] = {0};  // storage/processing buffer 2
uint16_t i2s_data3[FINAL_SIZE] = {0}; // storage buffer 3 - this is after the drop has been detected.
uint16_t i2s_data_dummy[PROC_SIZE] = {0}; // dump data to here when not actively sampling
bool buf_sel = false;


  static QueueHandle_t i2s_queue;




TaskHandle_t S_BAT = NULL;
TaskHandle_t JSON_T = NULL;
TaskHandle_t S_acc_offset = NULL;
TaskHandle_t BUT = NULL;
TaskHandle_t DET = NULL;

#define Bat_pin 39

// define the tasks
void JSON_Task( void *pvParameters );
void Sample_Bat( void *pvParameters );
void Get_acc_offset( void *pvParameters );
void Butterworth_Filter( void *pvParameters );
void Impact_Detection( void *pvParameters );
// define the I2S setup function.
void setupI2S();

bool Sample = false;
bool Sample_bat = false;
bool Buf_sel = false;


const int ADC_SAMPLES_COUNT = 1020;
int Trig_loc = 200;
double ACC_G[ADC_SAMPLES_COUNT] = { 0 };
double Trig_offset_G = 15;  // This is level of acceleration in Gs that the unit gets triggered.
int TRIGGER_VAL = 0;
bool Trigger = false;

String JSON;
int Trigger_offset = 100;
int Samples_Remaining = 0;
int glob_count = 0;

const char* PARAM_INPUT_1 = "acc";
const char* PARAM_INPUT_2 = "bat";


// Assign ssid and password for ESP32 AP
const char *ssid = "Testing";
const char *password = "12345678";
#define WIFI_TIMEOUT_MS 20000;


double ADC_TO_V = 1240.91;  // divide by this number to convert ADC input to volts
double Bat_correction = 4.025; // converts back correct battery voltage due to voltage divider
double fudge_factor = 1.234; // slight correction factor based on real world test results. 
double ACC_C = 500*fudge_factor; // Multiply by this conversion factor to go from volts to Gs.  Currently setup for 100mV/G accelerometer.  will need to update for final 2mV/G ACC.
double ACC_offset = 0; //  This is calculated offset in Volts.  This parameter gets updated in software when starting up.
double ACC_offset_G; // Offset in Gs
int Bat_in = 0;
double Bat_V = 0;
int Acc_state = 0;  // 0 is idle or don't take a reading.  1 is actively take the reading, 2 is ready to send the data to update the webpage.
int Bat_state = 0;  // 0 is not taking the reading, 1 is take a reading, 2 is send the reading


AsyncWebServer server(80);

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons ="";
    String outputStateValue = "1";
    String BatStateValue = "1";
    buttons+= "<h4>Sampling <span id=\"Acc_outputState\"></span></h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"1\" " + outputStateValue + "><span class=\"slider\"></span></label>";
    buttons+= "<h4>Get Battery Voltage <span id=\"Bat_outputState\"></span></h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + BatStateValue + "><span class=\"slider\"></span></label>";
    return buttons;
  }
  return String();
}


void setup() {
  Serial.begin(115200);
  Serial.println("starting ESP32");
  delay(1000);

  if(!SPIFFS.begin()){ 
    Serial.println("An Error has occurred while mounting SPIFFS");  
  }

Serial.println("Wifi about to be set up");
WiFi.mode(WIFI_AP);

  WiFi.softAP(ssid, password);
  Serial.println("Wait 100 ms for AP_START...");
  delay(100);
  
  Serial.println("Set softAPConfig");
  IPAddress Ip(192, 168, 4, 4);
  IPAddress NMask(255, 255, 255, 0);
  WiFi.softAPConfig(Ip, Ip, NMask);
  
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
//Turn off wifi power saving mode to allow i2s to work.
WiFi.setSleep(false);


Serial.println("First server call");
server.serveStatic("/", SPIFFS, "/");

Serial.println("Second server call");
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/index.html", String(), false, processor);
});



 // Send a GET request to <ESP_IP>/update?state=<inputMessage>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage1;
    String inputMessage2;
    String inputParam;
    if (request->hasParam(PARAM_INPUT_1)){
      Serial.println(request->getParam(PARAM_INPUT_1)->value());
    }
    else{
      Serial.println("No Param 1");
    }
    if (request->hasParam(PARAM_INPUT_2)){
      Serial.println(request->getParam(PARAM_INPUT_2)->value());
    }
    else{
      Serial.println("No Param 2");
    }
    // GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
     if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
      inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
      inputParam = PARAM_INPUT_1;
      Serial.print("INPUT Bat State: ");
      Serial.println(inputMessage1);
      Serial.print("INPUT Acc State: ");
      Serial.println(inputMessage2);
      Serial.print("Curent Bat State: ");
      Serial.println(Bat_state);
      Serial.print("CURRENT Acc State: ");
      Serial.println(Acc_state);
      if (Bat_state != 2){
      Bat_state = inputMessage1.toInt();  // 0 is Don't sample, 1 is Sample for battery
      Serial.println(Bat_state);
      }
      if(Acc_state != 2 && Acc_state != 3){
      Acc_state = inputMessage2.toInt();  // 0 is Don't Sample, 1 is Sample for accelerometer
      }
      if (Acc_state == 1){
      Sample = true;
      Serial.print("Sampling on");
      }
     if (Bat_state == 1){
      Serial.println("Sampling Battery");
      vTaskResume( S_BAT );
     }
    }
    else {
      inputMessage1 = "No message sent";
      inputMessage2 = "No message sent";
      inputParam = "none";
    }
    Serial.print("The input Message is ");
    Serial.print(inputMessage1);
    Serial.print(" and ");
    Serial.println(inputMessage2);
    request->send(200, "text/plain", "OK");
  });

  // Send a GET request to <ESP_IP>/state
  server.on("/state", HTTP_GET, [] (AsyncWebServerRequest *request) {
    request->send(200, "text/plain", String(Acc_state) +","
    + String(Bat_state));
  });

  // Send graph data for drop
 server.on("/readings", HTTP_GET, [] (AsyncWebServerRequest *request){
  //Serial.println(JSON);
  if (Acc_state == 3){
  request->send(200, "text/plain", JSON);
  Acc_state = 4;
  }
  else{
    request->send(200,"text/plain","Waiting");
  }
 });

 server.on("/voltage", HTTP_GET, [] (AsyncWebServerRequest *request){
  if(Bat_state == 2){
  request->send(200, "text/plain", String(Bat_V));
  Bat_state = 0;
  }
  else{
    request->send(200, "text/plain" , "Waiting");
  }
 });

 
 server.begin();
 Serial.println("Server Started");

  pinMode(Bat_pin, INPUT);
  adcAttachPin(Bat_pin);
  analogSetAttenuation(ADC_11db);


 Serial.println("Setting up Input I2S");
     setupI2S();
     Serial.println("I2S input setup completed");


Serial.println("Starting Sample bat Task");
  xTaskCreatePinnedToCore(
    Sample_Bat
    ,  "Sample_Bat"
    ,  8192  // Stack size
    ,  NULL
    ,  3  // Priority
    ,  &S_BAT 
    ,  NULL);
vTaskSuspend( S_BAT );

Serial.println("Starting JSON Task");
xTaskCreatePinnedToCore(
    JSON_Task
    ,  "JSON_Task"   // A name just for humans
    ,  8192*4  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  3  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  &JSON_T 
    ,  NULL);
       vTaskSuspend( JSON_T );
Serial.println("Starting Get ACC Offset Task");
  xTaskCreatePinnedToCore(
    Get_acc_offset
    ,  "Get_acc_offset"
    ,  8192  // Stack size
    ,  NULL
    ,  2  // Priority
    ,  &S_acc_offset 
    ,  NULL);
    delay(50);
Serial.println(" Starting Butterworth Filter Task");
  xTaskCreatePinnedToCore(
    Butterworth_Filter
    ,  "Butterworth_Filter"
    ,  8192*2  // Stack size
    ,  NULL
    ,  3  // Priority
    ,  &BUT 
    ,  NULL);
vTaskSuspend( BUT );
    Serial.println(" Starting Impact Detection Task");
  xTaskCreatePinnedToCore(
    Impact_Detection
    ,  "Impact_Detection"
    ,  8192  // Stack size
    ,  NULL
    ,  2  // Priority
    ,  &DET 
    ,  NULL);

}

void loop() {

}




void JSON_Task( void *pvParameters ){
        (void) pvParameters;
       // portENTER_CRITICAL_ISR(&timerMux);
        for(;;)
        {
          Serial.println("JSON Task Running");
          JSON = "{\"data\": [ ";

         for (uint16_t w = 0; w < 1000; w++){ 
          JSON = JSON + (String(ACC_G[w],2)) + ", ";
          }
          int len = JSON.length();
          Serial.println(JSON[len-2]);
          JSON[len-1] = '}';
          JSON[len-2] = ']';
          Serial.println(JSON[len-2]);
          Serial.println(JSON[len-1]);
          
          Acc_state = 3;
         
          vTaskSuspend( NULL );
        
       }
}

void Sample_Bat( void *pvParameters ){
  (void) pvParameters;
    for(;;)
    {
      Serial.println("Sample Bat Running");
      Bat_in = analogRead(Bat_pin);
      Bat_V = Bat_in / ADC_TO_V * Bat_correction;
      Serial.print("Battery Voltage is ");
      Serial.println(Bat_V);
      Bat_state = 2;
      Serial.print("Bat State: ");
      Serial.println(Bat_state);
      vTaskSuspend( NULL );
    }
}

void Get_acc_offset( void *pvParameters ){
  (void) pvParameters;
    for(;;)
    {
      size_t bytesRead = 0;
      // Read some data from the input pin.
      Serial.println("Starting Get_acc_offset");
     i2s_event_t evt;

     if (xQueueReceive(i2s_queue, &evt, 1) == pdPASS){
       
       if (evt.type == I2S_EVENT_RX_DONE){
          i2s_read(I2S_NUM_0, i2s_data_dummy, sizeof(i2s_data_dummy), &bytesRead, portMAX_DELAY);
           Serial.println(bytesRead);
          //i2s_adc_disable(I2S_NUM_0);
      
          int counter = 0;
     
         for (uint16_t k = 0; k < PROC_SIZE; k++){
         counter++;
          Serial.printf("The count is %i is: %u \n", counter, i2s_data_dummy[k]);
          ACC_offset = ACC_offset + i2s_data_dummy[k];
      
        }
         ACC_offset = ACC_offset/ counter;
         ACC_offset_G = ACC_offset  / ADC_TO_V * ACC_C;
        TRIGGER_VAL = (ACC_offset_G - Trig_offset_G) / ACC_C * ADC_TO_V;
     
         Serial.println();
        Serial.print(" The accelerometer calibration constant is ");
        Serial.println(ACC_offset);
        Serial.println();
        Serial.print("The accelerometer Trigger Value is: ");
        Serial.println(TRIGGER_VAL);
        vTaskSuspend( NULL );
       }
     }
     
    // vTaskResume(BUT);
     
    }
}

void Impact_Detection( void *pvParameters ){
    (void) pvParameters;
    for(;;)
    { 
        
        i2s_event_t evt;
        if (xQueueReceive(i2s_queue, &evt, 1) == pdPASS){

          if (evt.type == I2S_EVENT_RX_DONE){
        
            
            // Read the data to a buffer
            size_t bytesRead = 0;
           
            i2s_read(I2S_NUM_0, i2s_data, sizeof(i2s_data), &bytesRead, portMAX_DELAY);


            // if sampling is turned on, do something with the data just read.
            if (Sample == true){
               // filter for random anomolies
                for (uint16_t i = 0; i < PROC_SIZE; i++){
                  if(i > 1 && i < PROC_SIZE-2){
                  int ave1 = (i2s_data[i-2]+i2s_data[i-1]+i2s_data[i+1]+i2s_data[i+2])/4;
                  if (abs(i2s_data[i]-ave1) > 5){
                    i2s_data[i] = ave1;
                  }
                  }
                  else if(i <= 2){
                    int ave2 = (i2s_data[i+1]+i2s_data[i+2]+i2s_data[i+3]+i2s_data[i+4])/4;
                    if (abs(i2s_data[i]-ave2) > 5){
                    i2s_data[i] = ave2;
                  }
                  }
                  else if(i>=PROC_SIZE-2){
                    int ave2 = (i2s_data[i-1]+i2s_data[i-2]+i2s_data[i-3]+i2s_data[i-4])/4;
                    if (abs(i2s_data[i]-ave2) > 5){
                    i2s_data[i] = ave2;
                  }
                  }
                }
              if (Trigger == false){
              
               
               //Check for a trigger
               for (uint16_t i = 0; i < PROC_SIZE; i++){
                //Serial.println(i2s_data1[i]);
                 if (i2s_data[i] < TRIGGER_VAL){
                   Trig_loc = i;
                    Trigger = true;
                    
                    Serial.print("this is the info that triggered it: ");
                    Serial.println(i2s_data[i]);
                    Serial.print("Trigger location:  ");
                    Serial.println(i);
                    if (Trig_loc < Trigger_offset){
                      Samples_Remaining = FINAL_SIZE - PROC_SIZE - (Trigger_offset - Trig_loc);
                    }
                    else{
                      Samples_Remaining = FINAL_SIZE - (PROC_SIZE - Trig_loc + Trigger_offset);
                    }
                    glob_count = 0; // Resetting a global counter to count up to Samples Remaining.
                    //Serial.print("Number of samples remaining:   ");
                    //Serial.println(Samples_Remaining);
                    break;
                  }
                }
                if (Trigger == false){
                    for (uint16_t i = 0; i < PROC_SIZE; i++){
                      i2s_data1[i] = i2s_data[i];
                    }
                 }
                else{
                  for (uint16_t i = 0; i < PROC_SIZE; i++){
                      i2s_data2[i] = i2s_data[i];
                  }
                }
              }
              else{
                int happened = 0;
                for (uint16_t i = 0; i < PROC_SIZE; i++){
                  if (glob_count < Samples_Remaining){
                    i2s_data3[glob_count++] = i2s_data[i];
                    //happened++;
                    //Serial.print("This happened: ");
                    //Serial.println(happened);
                  }
                  else{
                    //Serial.print("global counter value:  ");
                    //Serial.println(glob_count);
                    Trigger = false;
                    Sample = false;
                    vTaskResume( BUT );
                    break;
                  }   
                }
              }
              }
          }
        }
    }
}




void Butterworth_Filter( void *pvParameters ){
    (void) pvParameters;
    for(;;)
    {

      int count = 0;
      if(Trig_loc < Trigger_offset){
        for(uint16_t x = (PROC_SIZE - (Trigger_offset - Trig_loc)); x < PROC_SIZE; x++){

          ACC_G[count++] = -(i2s_data1[x] - ACC_offset) / ADC_TO_V * ACC_C;

         }
        for (uint16_t j = 0; j < PROC_SIZE; j++){

          ACC_G[count++] = -(i2s_data2[j] - ACC_offset) / ADC_TO_V * ACC_C;

          }
      }
      else{
        for (uint16_t k = (Trig_loc-Trigger_offset); k < PROC_SIZE; k++){

         ACC_G[count++] = -(i2s_data2[k]-ACC_offset) / ADC_TO_V * ACC_C;

         }
        } 
      Serial.print("Count 1 is: ");
      Serial.println(count);
      for (uint16_t q = 0; q < Samples_Remaining; q++){

        ACC_G[count++] = -(i2s_data3[q] - ACC_offset) / ADC_TO_V * ACC_C; 

       }
       Serial.println(" Processing Complete. ");
       Serial.print(" The count is: ");
       Serial.println(count);

      

      double temp[count] = { 0 };
      
      double a[3] = {0};
      double b[3] = {0};
      // Second order butterworth coefficients
      // Fc = 2077.5 Hz
      // Fs = 20000 Hz
      a[0] = 0.071893;
      a[1] = 0.143786;
      a[2] = a[0];
      b[1] = 1.111586;
      b[2] = -0.399156;

      // Set temp to ACC_G
      for (int j = 0; j < count; j++){
        temp[j] = ACC_G[j];
      }

      // First pass in forward direction
      for (int i = 2; i <= (count - 1); i++){ 
        ACC_G[i] = (a[0]*temp[i]) + (a[1]*temp[i-1]) + (a[2]*temp[i-2]) + (b[1]*ACC_G[i+1]) + (b[2]*ACC_G[i+2]);
      }

      // Set temp to intermediate ACC_G
      for (int j = 0; j < count; j++){
        temp[j] = ACC_G[j];
      }


     
      // Second pass in backward direction
      for (int i = (count - 3); i >= 0; i--){
        ACC_G[i] = (a[0]*temp[i]) + (a[1]*temp[i+1]) + (a[2]*temp[i+2]) + (b[1]*ACC_G[i+1]) + (b[2]*ACC_G[i+2]); 
      }


      // Run the next task which processes the data and gets it sent to the computer
      vTaskResume( JSON_T );
      vTaskSuspend( NULL );
    }
      
}



void setupI2S() {
  Serial.println("Configuring I2S...");
  esp_err_t err;
  // The I2S config as per the example
  const i2s_config_t i2s_config = { 
      .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
      .sample_rate = samplingFrequency,                        
      .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // 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_I2S_MSB,
      .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,     // Interrupt level 1
      .dma_buf_count = NUM_BUFS,                           // number of buffers
      .dma_buf_len = SAMPLEBLOCK,                     // samples per buffer
      .use_apll = false//,
      //.tx_desc_auto_clear = false,
      //.fixed_mclk = 0
  };

 err = adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_0); //step 1
  if (err != ESP_OK) {
    Serial.printf("Failed setting up adc channel: %d\n", err);
    while (true);
  }
 err = adc1_config_width(ADC_WIDTH_BIT_12);
 if (err != ESP_OK) {
    Serial.printf("Failed setting up adc width: %d\n", err);
    while (true);
  }
 err = adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_11);
 if (err != ESP_OK) {
    Serial.printf("Failed setting up adc channel attenuation: %d\n", err);
    while (true);
  }

  err = i2s_driver_install(I2S_NUM_0, &i2s_config,  NUM_BUFS, &i2s_queue);  //step 2
  if (err != ESP_OK) {
    Serial.printf("Failed installing driver: %d\n", err);
    while (true);
  }

  err = i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT);
    if (err != ESP_OK) {
    Serial.printf("Failed setting up adc mode: %d\n", err);
    while (true);
  }

  i2s_adc_enable(I2S_NUM_0);
  
  Serial.println("I2S driver installed.");
}


Who is online

Users browsing this forum: No registered users and 65 guests