How to use a muscle sensor like a button

Hello everybody! I am new to arduino and this is my first forum post ever, so please excuse me if I make mistakes. (I hope this is the right section :cold_sweat: )
I have the following problem: I recently got a muscle sensor (detaills are listed at the end) which sends different voltage values to the A1 pin of my arduino micro pro clone. With the simplest possible code, the signal that I get in the serial plotter when I flex my muscle looks like this: (I hope the inserted pictures are visible!)

#define SensorPin1      A1

void setup() {
}

void loop() {
int val = analogRead(SensorPin1);
Serial.println(val);
delay(10); //a sampling frequency of 100Hz is good enough for my purpose
}

This looks pretty good already, however, I decided to smoothen this signal with a handy algorithm that I found in the forum (digitalSmooth from Paul Badger). The new code looks like this:

#define SensorPin1      A1
#define LED1 9

int ledstate = LOW;               //variables I need later
int static_analog_dta   = 0;
int threshold = 0;
int delta = 20;

// variables from the digitalSmooth function:
#define filterSamples  33               // filterSamples should  be an odd number, no smaller than 3
int sensSmoothArray1 [filterSamples];   // array for holding raw sensor values for sensor1
int rawData1, smoothData1;              // variables for sensor1 data


void setup() {

  Serial.begin(115200);

  long sum = 0;
  //This for loop, we read and add the values of A0 1100 times with an sampling frequency of (almost) 1ms.
  //By Deviding the sum through 1100 we get the average background noise that is read by the sensor when it is idle.
  for (int j = 0; j < 1100; j++)
  {
    sum += analogRead(SensorPin1);
    delay(1);
  }
  sum /= 1100;

  static_analog_dta = sum;
  threshold = static_analog_dta + delta; //I want the LED to turn on when a certain threshold is breached. The delta determines how far above the backround noise this threshold lays.

  Serial.print("static_analog_dta = ");
  Serial.println(static_analog_dta);
  pinMode(LED1, OUTPUT);
  digitalWrite(LED1, ledstate);
}
void loop() {      // test the digitalSmooth function
  rawData1 = analogRead(SensorPin1);
  smoothData1 = digitalSmooth(rawData1, sensSmoothArray1);  // Here we give the smoothing function the raw data and the array to which the smoothed data is returned. Every sensor you use with digitalSmooth needs its own array!

  if (smoothData1 > threshold)
  { ledstate = !ledstate;
    digitalWrite(LED1, ledstate);
  }
  else
  {
    digitalWrite(LED1, !ledstate);
  }

  Serial.println(smoothData1);

  delay(10); //sampling frequency 100Hz

}

//digital smoothing frunction from Paul
int digitalSmooth(int rawIn, int *sensSmoothArray) {    // "int *sensSmoothArray" passes an array to the function - the asterisk indicates the array name is a pointer
  int j, k, temp, top, bottom;
  long total;
  static int i;
  // static int raw[filterSamples];
  static int sorted[filterSamples];
  boolean done;

  i = (i + 1) % filterSamples;    // increment counter and roll over if necc. -  % (modulo operator) rolls over variable
  sensSmoothArray[i] = rawIn;                 // input new data into the oldest slot


  for (j = 0; j < filterSamples; j++) { // transfer data array into anther array for sorting and averaging
    sorted[j] = sensSmoothArray[j];
  }

  done = 0;                // flag to know when we're done sorting
  while (done != 1) {      // simple swap sort, sorts numbers from lowest to highest
    done = 1;
    for (j = 0; j < (filterSamples - 1); j++) {
      if (sorted[j] > sorted[j + 1]) {    // numbers are out of order - swap
        temp = sorted[j + 1];
        sorted [j + 1] =  sorted[j] ;
        sorted [j] = temp;
        done = 0;
      }
    }
  }


  // throw out top and bottom 15% of samples - limit to throw out at least one from top and bottom
  bottom = max(((filterSamples * 15)  / 100), 1);
  top = min((((filterSamples * 85) / 100) + 1  ), (filterSamples - 1));   // the + 1 is to make up for asymmetry caused by integer rounding
  k = 0;
  total = 0;
  for ( j = bottom; j < top; j++) {
    total += sorted[j];  // total remaining indices
    k++;
  }


  return total / k;    // divide by number of samples
}

However, I want to use this sensor as an on/off switch for a motor. A peak of my signal shall act like a button press.
I tried solving this in various ways, but I am stuck. The solution that comes closest to what I want is the one above with the LED and the threshold:

 if (smoothData1 > threshold)
  { ledstate = !ledstate;
    digitalWrite(LED1, ledstate);
  }
  else
  {
    digitalWrite(LED1, !ledstate);
  }

But that does not work, since the LED does not always react correctly, or reacts twice.
Is it possible to give the signal read a cool-down without using the delay command?
Do people with more experience know an elegant solution for this?
Any help is appreciated! :roll_eyes:

Details:
Arduino clone:
Pro Micro ATmega32U4 5V 16MHz from Ali express https://www.aliexpress.com/item/32775200720.html?spm=a2g0s.9042311.0.0.27424c4d2f65RQ
Sensor: Grove EMG detector from Seeed Studio https://www.seeedstudio.com/Grove-EMG-Detector-p-1737.html

Damnit. I actually forgot to state my problem.
So: I have a muscle sensor. I get signal peaks when I flex my muscles. How can I use these signal peaks as action events that change a boolean from true to false and vice versa?

My current if condition solves this problem very unreliably.

In the broader project, I want to use the peaks of the signal like a pushbutton. When the button gets pressed once, a motor shall be activated and continue to run untill the button is pressed again.

Any ideas? Will I need some complicated DSP magic or is there a simpler solution with interrupts e.g.?

Any help is appreciated!

I have a project using a Mayoware http://www.advancertechnologies.com/p/myoware.html.

I would begin by graphing the signals using the Arduino IDE's Serial Plotter. It's crude but will give you graphed display of the movement made. I'd then learn how the movement I wan to do the thing relates to the plotted data points. I'd then see how I could relate the data points with running code based upon the selected data points to perform a selected action.

Thank you for responding! :smiley:
That is exactly what I tried already, but it does not work the way it should.
When plotting the smoothed signal, my muscle flexes produce some big nice spikes that I theoretically should be able to detect with the first derivative and the second derivative and a threshhold.
The problem is: I don't - at least not always.

I know, it is just a mathematical / programming problem, but I am not yet smart enough to find the solution.

Do you know a way to reliable way to detect peaks in an analog signal?

Any help is appreciated! :slight_smile:

place to storesomething = zero

getadreading = the ad to d reading

if storesomething < getadreading then its a new high so store it in storesomething.

of course there will be code to detect when the reading starts to drop and code to restart peak detection

AND, of course, one could simply enter the words "arduino peak detection" into a internet search engine and get some results where people have already solved this thing of peak detection.

I am starting a project where I am going to detect heartbeat peaks to determine time periods between beats. So, in a way, we will be working towards the same goal.

I have started with this Peak Detection Library. Right now I am looking over the code.

I will be using a a SImpleKalmanFilter to smooth the code before sending the data to a peak detection engine.


That peak detection library will not work well with a Uno.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.