espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.56k stars 7.4k forks source link

Slow ADC Sampling Rate #2657

Closed bharath1000 closed 5 years ago

bharath1000 commented 5 years ago

Hardware:

Board: Adafruit Huzzah feather board, wroom-32 IDE name: Arduino IDE

Description:

Hi all,

I'm pretty novice at both arduino, esp-idf, and programming in general, but I am working on a project to take EMG signals (muscle activity) and send it over WiFi to a computer for analysis. So far, I seem to have gotten it working overall, except for the fact that I cannot get a sample rate faster than 125 Hz! Ideally, I need to reach at least 1000 Hz sample rate to get a good signal.

I have tried interrupts, and rtos for multitasking, but nothing has worked. I have even tried an empty loop simply printing micros() since the last loop, and even that only goes ~600 Hz Everywhere I look online, everyone is able to reach 30k+ Hz sample rate. What am I doing wrong?

I've attached my code that uses interrupts (again, much of this is pasted from resources online). Using the adafruit feather board, wroom-32

Thank you!



//Change the code below by your sketch
// Include Libraries
#include "Arduino.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include "SparkFunLSM6DS3.h"
#include "Wire.h"
#include "SPI.h"
#include <MadgwickAHRS.h>
#include "esp_wpa2.h" //wpa2 library for connections to Enterprise networks

LSM6DS3 myIMU(SPI_MODE,21); //Default constructor is I2C, addr 0x6B
Madgwick filter;
float EMA_a = 0.3;    //initialization of EMA alpha
uint16_t EMA_S = 0;        //initialization of EMA S

#define EAP_ANONYMOUS_IDENTITY "anonymous@example.com"
#define EAP_IDENTITY "******"
#define EAP_PASSWORD "*******"
const char* ssid = "*****"; // Eduroam SSID
//const char* host = "arduino.php5.sk"; //external server domain for HTTP connection after authentification
uint16_t counter = 0;
bool wifistat = false;
WiFiClient client;

unsigned long microsPerReading, microsPrevious, timer, microfreq, currentaccel, currentemg, currentprint, previousaccel=millis(), previousemg=millis(), previousprint=millis();

//// Set these to your desired credentials.
//const char *ssid = "GGesp32";
//const char *password = "capstone";

WiFiServer server(80);

//Timer Interrupts
hw_timer_t *acceltimer = NULL;
hw_timer_t *emgtimer = NULL;
hw_timer_t *printtimer = NULL;
bool updateEMG = false;
bool updateAccel = false;
bool updatePrint = false;

// Initialize Analog Pins
uint16_t EMGLeftRA_pin = 39; //A3
uint16_t EMGRightRA_pin = 34; //A2
uint16_t EMGLeftOb_pin = 36; //A4
uint16_t EMGRightOb_pin = 33; //33
uint16_t EMGErect_pin = 32; //32

// Initialize EMG Aquisition Variables
uint16_t EMGleftRA=0;
uint16_t EMGrightRA=0;
uint16_t EMGleftob=0;
uint16_t EMGrightob=0;
uint16_t EMGerect=0;

// Initialize Mapping for EMG data
uint16_t Min = 0;
uint16_t Max = 4095;
uint16_t Map_Min= 0;
uint16_t Map_Max= 2200;

//Initialize Accelerometer Data
float AccelX=0;
float AccelY=0;
float AccelZ=0;
float roll, pitch, heading;

//Initialize Gyrometer Data
float GyroX=0;
float GyroY=0;
float GyroZ=0;

/*
 * setup function
 */

double modifiedMap(double x, double in_min, double in_max, double out_min, double out_max)
{
 return round(((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)*10)/10;
}

void IRAM_ATTR onEMGTimer(){
  updateEMG = true;
}

void IRAM_ATTR onAccelTimer(){
  updateAccel = true;
}

void startTimer(){
  acceltimer = timerBegin(0,80,true);
  emgtimer = timerBegin(3,80,true);
  timerAttachInterrupt(acceltimer,&onAccelTimer,true);
  timerAttachInterrupt(emgtimer,&onEMGTimer,true);
  timerAlarmWrite(acceltimer,1000000/26,true);
  timerAlarmWrite(emgtimer,10000000/1000,true);
  timerAlarmEnable(acceltimer);
  timerAlarmEnable(emgtimer);
}

// Setup the essentials for your circuit to work. It runs first every time your circuit is powered with electricity.
void setup() 
{
  Serial.begin(1000000);
  delay(10);
  Serial.println();
  Serial.print("Connecting to network: ");
  Serial.println(ssid);
  WiFi.disconnect(true);  //disconnect form wifi to set new wifi connection
  WiFi.mode(WIFI_STA); //init wifi mode
 esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_ANONYMOUS_IDENTITY, strlen(EAP_ANONYMOUS_IDENTITY)); 
  esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY));
  esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD));
  esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
  esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
  WiFi.begin(ssid); //connect to wifi
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    counter++;
    if(counter>=60){ //after 30 seconds timeout - reset board
      ESP.restart();
    }
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address set: "); 
  Serial.println(WiFi.localIP()); //print LAN IP

  myIMU.settings.gyroRange = 125;   //Max deg/s.  Can be: 125, 245, 500, 1000, 2000
  myIMU.settings.gyroSampleRate = 26;   //Hz.  Can be: 13, 26, 52, 104, 208, 416, 833, 1666
  myIMU.settings.gyroBandWidth = 50;  //Hz.  Can be: 50, 100, 200, 400;

  myIMU.settings.accelRange = 2;      //Max G force readable.  Can be: 2, 4, 8, 16
  myIMU.settings.accelSampleRate = 26;  //Hz.  Can be: 13, 26, 52, 104, 208, 416, 833, 1666, 3332, 6664, 13330
  myIMU.settings.accelBandWidth = 50;  //Hz.  Can be: 50, 100, 200, 400;

  myIMU.begin();
  filter.begin(26);

// initialize variables to pace updates to correct rate
  microsPerReading = 1000000 / 26;
  microsPrevious = micros();

// //Start Servers
  server.begin();

// Start Timers
  startTimer();

}

// Main logic of your circuit. It defines the interaction between the components you selected. After setup, it runs over and over again, in an eternal loop.
void loop() 
{

  if (WiFi.status() == WL_CONNECTED && wifistat == false) { //if we are connected to Eduroam network
    counter = 0; //reset counter
    Serial.println("Wifi is still connected with IP: "); 
    Serial.println(WiFi.localIP());   //inform user about his IP address
    wifistat = true;
  }else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
    WiFi.begin(ssid); 
    wifistat = false;     
  }
  while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
    delay(500);
    Serial.print(".");
    counter++;
    if(counter>=60){ //30 seconds timeout - reset board
    ESP.restart();
    }
  }
  client = server.available();
  delay(1);
  if (client) {                             // if you get a client,
    //Serial.println("New Client.");           // print a message out the serial port
    //String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected

      if (updateAccel == true){
        // Defining Positional Data
        AccelX=myIMU.readFloatAccelX(); 
        AccelY=myIMU.readFloatAccelY(); 
        AccelZ=myIMU.readFloatAccelZ();

        // Defining Positional Data

        GyroX=myIMU.readFloatGyroX();
        GyroY=myIMU.readFloatGyroY(); 
        GyroZ=myIMU.readFloatGyroZ();   

        filter.updateIMU(GyroX,GyroY,GyroZ,AccelX,AccelY,AccelZ);

        roll = filter.getRoll();
        pitch = filter.getPitch();
        heading = filter.getYaw();

        EMA_S = (EMA_a*heading)+((1-EMA_a)*EMA_S);
        heading = heading - EMA_S;

        AccelX=heading;
        AccelY=pitch;
        AccelZ=roll;

        updateAccel = false;
      }

      if (updateEMG ==true){
//      //Method 1 EMG, adding false gain
      EMGleftRA=analogRead(EMGLeftRA_pin);
      EMGrightRA=analogRead(EMGRightRA_pin);
      EMGleftob=analogRead(EMGLeftOb_pin);
      EMGrightob=analogRead(EMGRightOb_pin);
      EMGerect=analogRead(EMGErect_pin);

      EMGleftRA = constrain(EMGleftRA*1.814,Min,Max);
      EMGrightRA = constrain(EMGrightRA*1.814,Min,Max);
      EMGleftob = constrain(EMGleftob*2.165,Min,Max);
      EMGrightob = constrain(EMGrightob*2.165,Min,Max);
      EMGerect = constrain(EMGerect*2.268,Min,Max);

      // Maping vaues from 0-4095 to 0-2200Mv
      EMGleftRA=modifiedMap(EMGleftRA,Min,Max,Map_Min,Map_Max);
      EMGrightRA=modifiedMap(EMGrightRA,Min,Max,Map_Min,Map_Max);
      EMGleftob=modifiedMap(EMGleftob,Min,Max,Map_Min,Map_Max);
      EMGrightob=modifiedMap(EMGrightob,Min,Max,Map_Min,Map_Max);
      EMGerect=modifiedMap(EMGerect,Min,Max,Map_Min,Map_Max);

    // SENDING RAW EMG DATA TO LABVIEW

    // Initialize buffer for the ASCII String
    char a[6]="";
    char b[6]="";
    char c[6]="";
    char d[6]="";
    char e[6]="";

    // Initializing Buffer for ASCII String for Accel Data
    char Ax[6]="";  
    char Ay[6]="";  
    char Az[6]="";

    // Initializing Buffer for ASCII String for Accel Data
    char Gx[6]="";  
    char Gy[6]="";  
    char Gz[6]="";

    // Converting RAW emg data to a string
      String str1= String(EMGleftRA);
      String str2= String(EMGrightRA);
      String str3= String(EMGleftob);
      String str4= String(EMGrightob);
      String str5= String(EMGerect);

      // Converting Accel Data to Strings
      String Axstr= String(AccelX);
      String Aystr= String(AccelY);
      String Azstr= String(AccelZ);

      // Converting Accel Data to Strings
      String Gyxstr= String(GyroX);
      String Gyystr= String(GyroY);
      String Gyzstr= String(GyroZ);

      // Converts 5 bytes of data in str to a char array e
      str1.toCharArray(a,5);
      str2.toCharArray(b,5);
      str3.toCharArray(c,5);
      str4.toCharArray(d,5);
      str5.toCharArray(e,5);

      // Converting Strings to Char Array
       Axstr.toCharArray(Ax,6);
       Aystr.toCharArray(Ay,6);
       Azstr.toCharArray(Az,6);

       // Converting Strings to Char Array
       Gyxstr.toCharArray(Gx,6);
       Gyystr.toCharArray(Gy,6);
       Gyzstr.toCharArray(Gz,6);
  //
  //    //Writing all to server EMG first then Accel then Gyro
      client.write(a);
      Serial.print(a);
      client.write(",");
      Serial.print(",");
      client.write(b);
      Serial.print(b);
      client.write(",");
      Serial.print(",");
      client.write(c);
      Serial.print(c);
      client.write(",");
      Serial.print(",");
      client.write(d);
      Serial.print(d);
      client.write(",");
      Serial.print(",");
      client.write(e);
      Serial.print(e);
      client.write(",");
      Serial.print(",");
      client.write(Ax);
      Serial.print(Ax);
      client.write(",");
      Serial.print(",");
      client.write(Ay);
      Serial.print(Ay);
      client.write(",");
      Serial.print(",");
      client.write(Az);
      Serial.println(Az);

//ENDING DATA TRANSMISSION 
        client.write("\r");

    }
    //close the connection
    client.stop();
    Serial.println("Client Disconnected.");
}
}
josefinHeilig commented 5 years ago

Hi, You did not set your boolean variable updateEMG = false; again in your if (updateEMG ==true){} condition, so this is always entered and executed, which slows it down. And I would try using AsyncUDP for the Wifi like in the example ESP32 Async UDP. The Wifi TCP protocol always has to wait for the handshake with the server and that possibly slows the main loop down. Best, Katrin

bharath1000 commented 5 years ago

Thank you for your reply!

yes sorry that is fixed in the actual running code, must have removed it when copying by accident. Running the code even without any networking, without any extra functions, just doing a single analog read in the loop and nothing else is maxing out at around 300 Hz. This is confusing to me as the documentation says that it can go up to 6KHz and while through googling I've seen people go even higher than that.

Below is an example of the code we're using (with most of the extra features cut out), still giving us a very slow sampling rate. We are tracking this sampling rate by using the micros() function.

` // Include Libraries

include "Arduino.h"

// Initialize Analog Pins uint16_t EMGLeftRA_pin = 39; //A3

// Initialize Mapping for EMG data uint16_t Min = 0; uint16_t Max = 4095; uint16_t Map_Min= 0; uint16_t Map_Max= 2200;

// Initialize EMG Aquisition Variables uint16_t EMGleftRA=0;

// Setup the essentials for your circuit to work. It runs first every time your circuit is powered with electricity. void setup() { Serial.begin(115200); delay(10); }

// Main logic of your circuit. It defines the interaction between the components you selected. After setup, it runs over and over again, in an eternal loop. void loop() {

EMGleftRA = analogRead(39);
delayMicroseconds(1);
EMGleftRA=constrain(EMGleftRA*1.814,Min,Max);
EMGleftRA=map(EMGleftRA,Min,Max,Map_Min,Map_Max);

Serial.println(micros());

} `

zekageri commented 5 years ago

Hi bharath1000! I hope you need some suggestions after all. I was struggeling with the analog read too and i managed to find a piece of code on a group in facebook a couple of weeks ago. I wanted to measure a 3Khz signal. The thing is if you want to measure a signal you need twice reading speed than the measured one. So the code in Arduino ide is capable to measure with 44Khz but unfortunatelly this is slows down everything on the esp except the measure. Because if we measure we have to disable all interrutps wich of course affect the wifi and things like that that are on your loop.

//================================ ADC Setup =================================

define CHANNEL 34

int offset = 1757; //middle DC level reading int vReal[1024]; const double samplingFrequency = 44000; //Hz unsigned int sampling_period_us; unsigned long microseconds; int samples = 1024; //================================ ADC Setup ================================= void setup(){ Serial.begin(115200); }

void loop(){

portDISABLE_INTERRUPTS();
microseconds = micros();
sampling_period_us = round(1000000*(1.0/samplingFrequency));

for(int i=0; i<samples; i++) { vReal[i] = analogRead(CHANNEL)/-offset/; while(micros() - microseconds < sampling_period_us){ } //empty loop microseconds += sampling_period_us; } portENABLE_INTERRUPTS();

}

Iam sending the measured data via websocket to a webserver for ChartJS visualization. Iam able to measure properly a 4Khz sinus or square wave signals with this. I hope i was able to help you a little bit. And if anyone know a better way iam listening :D

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 5 years ago

This stale issue has been automatically closed. Thank you for your contributions.