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 )
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!
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