How can I calculate a moving average in Arduino? - arduino

I have to add a moving average to my program which is working now with ultrasonic sensor with Arduino.
First screen of my code
Second screen of my code
The third screen of my code

I hope the following code will help you:
//Parameters
const int aisPin = A0;
const int numReadings = 10;
int readings [numReadings];
int readIndex = 0;
long total = 0;
//Variables
int aisVal = 0;
void setup() {
//Init Serial USB
Serial.begin(9600);
Serial.println(F("Initialize System"));
//Init AnalogSmooth
pinMode(aisPin, INPUT);
}
void loop() {
readAnalogSmooth();
Serial.print(F("ais avg : ")); Serial.println(smooth());
delay(200);
}
void readAnalogSmooth( ) { /* function readAnalogSmooth */
////Test routine for AnalogSmooth
aisVal = analogRead(aisPin);
Serial.print(F("ais val ")); Serial.println(aisVal);
}
long smooth() { /* function smooth */
////Perform average on sensor readings
long average;
// subtract the last reading:
total = total - readings[readIndex];
// read the sensor:
readings[readIndex] = analogRead(aisPin);
// add value to total:
total = total + readings[readIndex];
// handle index
readIndex = readIndex + 1;
if (readIndex >= numReadings) {
readIndex = 0;
}
// calculate the average:
average = total / numReadings;
return average;
}
The basic concept is the same, keep a fixed window size and shift the window after each reading.
Note: Please change the above code according to your need.

There are lots of ways to implement a moving/sliding average. One simple one that I've implemented is below, which works much the same way as HARSH's that he posted in his previous answer. This function, though, is a little more generic and can be used as-is for any data source and you can test it out on any platform. It also handles the startup case when values are first being populated. It is specific, though, to one source. So if you need a moving average for each of multiple sources, you'll have to duplicate the function or modify it to handle multiple sets of data. This uses float values for the data. Even if your data is integer, I'd suggest leaving the averaging data as float. Again, this is a simple averaging where all data values in the window have the same weight.
float movingAverage(float value) {
const byte nvalues = 8; // Moving average window size
static byte current = 0; // Index for current value
static byte cvalues = 0; // Count of values read (<= nvalues)
static float sum = 0; // Rolling sum
static float values[nvalues];
sum += value;
// If the window is full, adjust the sum by deleting the oldest value
if (cvalues == nvalues)
sum -= values[current];
values[current] = value; // Replace the oldest with the latest
if (++current >= nvalues)
current = 0;
if (cvalues < nvalues)
cvalues += 1;
return sum/cvalues;
}
The way you use it is fairly simple. Instead of calling, for example:
x = analogRead(DATA_PIN);
You would call:
x = movingAverage(analogRead(DATA_PIN));
And the movingAverage function does the rest for you. Inside the movingAverage function, you'll see a const value that defines the number of values used in the average. In the above case, it's 8.
You can use movingAverage on any sequence of values, so it doesn't have specific code inside it for reading pins. The way the movingAverage function works is that it keeps track of the last nvalues count of values you call it with and always returns the rolling average of those. It also avoids summing all the values in the window on each call by using a "delta" technique for the sum.

Related

How to convert the ANALOG value of a pulse sensor to BPM?

first of all I would like to apologise for not speaking English very well.
My problem is the following: I have an arduino sensor (the iDuino SE049). I need to transform the analog value into BPM value.
So first I have to eliminate the drift (slide, (I don't know the precise word)) that the sensor can encounter. To do this I am advised to :
❶Work on an array of X values
❷Calculate the average
❸Subtract the average from each of the values
After I need to find the optimal value for X with the curve on Arduino IDE (I work on 1.8.19 version).
(Start with X = 50 for the size of the array, my teacher told me that)
Once I have this average, I need to make the system robust to light changes.
Problem: The amplitude varies so we can't work with a fixed threshold.
The heart rate is hidden in temporal information. You just need to be able to measure the characteristic time.
Solution : I can work with a curve that always has maximum amplitude of 1, then find an ideal threshold.
❶Work on the table of X values after eliminating the drift.
❷Look for the maximum
❸Divide all values by this maximum
❹Split by a threshold value
And then I have to do the study to find the threshold value
I tried to do a code that didn't worked well for the drift/slide, and average of the signal. But it didn't worked well because I can't get the value of the average outside of my loop to substract it to the analog value readed by the sensor.
I want to get all the value readed by the sensor during a time of 20 milliseconds, thats why I use the function millis(). And all this values needs to be stored in my array.
#include <Arduino.h>
int Sensor = 0;
//#define start_read 0
float tab[20]={0};
float sum = 0;
int i;
void setup() {
Serial.begin(9600);
}
void loop ()
{
unsigned long currentTime=0;
while (millis() - currentTime > 20)
{
tab[i] = analogRead(Sensor);
i++;
sum += tab[i];
float average = sum/20;
Serial.println(average);
}
currentTime = millis();
i = 0;
}
I have another code to test, I should work but it's not I don't know why : The variable names are in French but you can copy it and change the name to work on it if it's clearer, I'll understand the changes.
#include <Arduino.h>
int sensor=A0, comp=1, var=0;
float bpm=0;
double temps;
const int nbr=50;
float tab[nbr]={0}, moyenne=0, maximum=0;
void setup() {
Serial.begin(9600);
pinMode(sensor, INPUT);
}
void loop() {
maximum=0;
moyenne=0;
for (int i = 0; i < nbr; i++)
{
tab[i]=analogRead(sensor);
moyenne=moyenne+tab[i];
delay(40);
}
moyenne=moyenne/nbr;
for (int i = 0; i < nbr; i++)
{
tab[i]=tab[i]-moyenne;
}
maximum=tab[0];
for (int i = 0; i < nbr; i++)
{
if (tab[i]>maximum)
{
maximum=tab[i];
}
}
for (int i = 0; i < nbr; i++)
{
tab[i]=tab[i]/maximum;
}
for (int i = 0; i < nbr; i++)
{
if (tab[i]>(0.950))
{
bpm++;
}
}
temps=(millis()/1000);
var=15*comp;
if (temps/(var)>=1)
{
bpm=bpm*4;
//Serial.setCursor(0,10);
Serial.print("BPM : ");
Serial.print(bpm);
Serial.println("♥️");
//Serial.display();
//Serial.clearDisplay();
comp=comp+1;
bpm=0;
}
}
I can't get the value of the average outside of my loop
average is declared inside the loop so when an iteration ends it goes out of scope. You should declare it outside the while loop.
I want to get all the value readed by the sensor during a time of 20 milliseconds
You probably want to get something like 1 value per millisecond because all the values readed in 20 milliseconds are not 20 also you may not be able to know the exact number of values as it depends on the sensor's sample rate among other things.
If you need to reed those values to get a fixed threshold (i.e. use the code to study some behavior and get a const that will be used in the second code), put the code in void setup() instead of void loop() otherwise it will loop forever.
void loop ()
{
unsigned long currentTime=0;
while (millis() - currentTime > 20)
{
tab[i] = analogRead(Sensor);
i++;
sum += tab[i];
float average = sum/20;
Serial.println(average);
}
currentTime = millis();
i = 0;
}
Here there are some problems. Every time void loop() ends and executes again currentTime its going to be deleted and declared again holding the value 0. This makes the while loop execute for an infinite amount of time causing i to increment over tab's length leading to memory errors.
Also analogRead() does not return a float (returns an integer between 0 and 1023).
That's what I found about the first code, but about the second one I don't understand what is the problem. It is a compiling error? You don't get the expected output in the serial monitor? If it's the last thing, what is the output?

How to control the speed of a stepper motor using PID control and ultrasonic sensor?

Little backstory:
I'm currently doing this project that deals with using two cars, called block A and B, which block B has to maintain a distance of 10 cm from block A using PID, PD, PI, or P. I'm using a PID. Block B uses an Arduino whereas Block A is controlled by the user by hand. Block B uses a unipolar stepper motor as the actuator and an ultrasonic sensor to sense the distance. My professor wants the motor to move in both directions and have varying speeds (slow, medium, and fast). I'm using brett's PID since I have used it before in my previous labs.
Problem:
I have an issue with how to create varying speeds for block B like intuitively I know that I want the B should move for example, fast if the car is greater than 20 cm, medium if the car is between 20cm and 14cm, and slow if it's between 14cm and 10cm. But I just can't use the input value retrieved from the sensor directly to control the motor as it would make it an open system. So I used the error retrieved from Brett's PID code to control the stepper motor. So far, I have gotten the directions to work by setting the myPID.SetOutputLimits(-800,800);. But as it tried to use the error to control the speed it would be impossible because the error always fluctuates at a specific distance. For example at 12cm, I would get either 800 or around 300. I'm currently confused about how to implement control of the speed of my stepper motor through PID and any help regarding this issue will be appreciated.
Code:
Code was through Arduino IDE.
#include "SR04.h"
#include <Stepper.h>
#include <PID_v1.h>
#define TRIG_PIN 7
#define ECHO_PIN 6
//intialization of Ultrasonic sensor
SR04 sr04 = SR04(ECHO_PIN,TRIG_PIN);
long s;
//intializing motor variables
int stepsPerRevolution = 2048;
int motorSpeed = 6;
Stepper myStepper (stepsPerRevolution, 8, 10, 9, 11);
//Declared PID variables
double Setpoint = 10; //desired temp value
double Input; //thermsitor
double Output; //DC motor
double Error;
//defined variables for PID parameters
double Kp=100, Ki=10, Kd=1;
//PID equation
PID myPID(&Input, &Output, &Setpoint, Kp, Kd, Ki, REVERSE);
void setup(){
Serial.begin(9600);
//setting PID
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-800,800);
//speed intialized
myStepper.setSpeed(motorSpeed);
}
void loop(){
s=sr04.Distance();
Input = s;
myPID.Compute();
Error = Input - Setpoint;
//Serial.print(Input);
//Serial.print(",");
//Serial.println(Setpoint);
Serial.println(Output);
//Serial.print(",");
//Serial.println(Error);
Error = Output;
//Away from Block B
if (0<Error<800){
myStepper.setSpeed(motorSpeed);
myStepper.step(-300);
} //slow speed
if (Error>=800){
myStepper.setSpeed(motorSpeed*2);
myStepper.step(-128);
} //fast speed
//Towards Block B
if (-800<Error<0) {
myStepper.setSpeed(motorSpeed);
myStepper.step(128);
} //slow speed
if (Error<=-800) {
myStepper.setSpeed(motorSpeed*2);
myStepper.step(128);
}//Fast speed
}
What you need to do is calcuulate how much you need to change your current speed to minimize the error in distance.
Your calculation for error is not in the right place.
void loop()
{
long s=sr04.Distance();
Input = s; // using global variables to pass values to your PID
// is not a good idea. Use function parameters instead.
// You are storing a 32 bit value in a 16 bit variable!!!
// That's only the start of your problems.
myPID.Compute();
Error = Input - Setpoint; //
Since we're starting with a major design flaw, I'll have to assume you'll fix that and change your PID code to accept and compute long integers both as input value as a function parameter, and as the type of its return value..
What you want to do is compute the PID from the error in distance from your set point, and then modulate the current speed accordingly. PIDs work best when used directly, using 7 speeds (1 stopped, 3 forward/3 backwards) is possible, but I don't think it'll give better results, I'll leave the exercise to you.
I haven't tried this, I don't have any cars on hand. This is a skeletoon of how I'd go about it. Tuning the PID should be what takes you the longest.
//...
// speeds are in RPMs.
long curSpeed = 0;
const long MAX_SPEED = XXX; // whatever you max speed is for your car.
const long MIN_NEG_SPEED = -XXX; // whatever you max speed is for your car going reverse.
const long MIN_SPEED = XXX; // below this absolute speed, we're stopped.
const int SLICE_TIME = 10; // time between readings and adjustments, in ms.
// you'll need to adjust this according to you minimum speed, and steps per turn.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is.
// you'll need to limit the output of your PID to match the acceleration your
// motors can handle for your particular car.
// returns the number of steps to run for our slice time.
long Steps(int speed)
{
if (-MIN_SPEED <= speed && speed <= MIN_SPEED)
return 0;
// compute number of steps for our slice time.
// choose slice time and minimum speed wisely!!
long steps = (SLICE_TIME * (speed * STEPS_PER_TURN)) / (60000L);
// for very low speeds. I've added this, because I'm unsure of the
// time domain behaviour of stepper library with less than 2 steps
if (-1 <= steps && steps <= 1)
{
if (speed < 0)
return -2;
else
return 2;
}
return int(steps);
}
void loop()
{
// You may want to filter the sr04 readings with a median of 5
// filter to limit input noise.
// You want to keep the car at a distance of 'set_point'
// from the leading car. distance_error is the error you want to
// minimize to zero by using the PID, and that's what should be
// the PID input.
//
// The way this works. We are rolling at speed curSpeed, we
// measure the error in distance from our set_point, feed that
// to the PID, then accelerate or decelerate by subtracting
// the output of the PID from the current speed.
//
// Note: you can add or subtract the PID to/from the current speed,
// the sign of the PID depends on you coefficients and sensor.
// I've used subtraction here because that's how you express
// negative feedback mathematically. In real life, we'll use what
// best fits our needs. Usually it's the operation that makes P
// positive.
long distance_error = sr04.Distance() - setPoint;
long pid_out = myPID.Compute(distance_error);
// increment or decrement the current speed to try and reduce the error.
long speed = curSpeed - pid_out; // As usual, PID coefficients do matter
// for this to work well.
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < MIN_NEG_SPEED)
speed = MIN_NEG_SPEED;
curSpeed = speed;
if (speed < 0)
speed = -speed;
myStepper.setSpeed(speed); // modulate speed
int steps = Steps(curSpeed);
if (steps)
myStepper.step(steps); // keep rolling.
}
I haven't tried to compile it either, so this may not compile as is. But most of the tricks and traps are covered, and this should give you a head start, if you want to go the PID route. But I think your professor will really wonder where that one came from :) Still, you should try and make it run, for fun.
The other way, without a PID, and using set speeds is much more straightforward. It may also be closer to what the is required by the exercise. The distance between cars will vary a bit more, of course. And it does not use a PID at all.
const int MAX_SPEED = 3;
int speed = 0; // value range is [-MAX_SPEED, +MAX_SPEED]
long RPMS[MAX_SPEED + 1] = { 0, 200, 400, 800 }; // in RPMs, assuming average speed will be around 400, in this case.
// For 3 speeds, the difference between speeds cannot be higher than max acceleration.
// You can add as many speeds as desired. More speeds = more precision.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is. MUST be 200 or more.
const int STEPS = STEPS_PER_TURN / 100; // 3.6° between speed adjustment.
// it is very small right now, so
// you will want to play with this value.
// this threshold gives some control over aceleration.
// and 'hardness' of distance tracking.
const long THRESHOLD = 0;
void loop()
{
// get the error in distance.
long distance_error = sr04.Distance() - setPoint;
// modulate speed.
if (distance_error > THRESHOLD)
++speed;
if (distance_error < -THRESHOLD)
--speed;
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < -MAX_SPEED)
speed = -MAX_SPEED;
long rpm = RPMS[(speed < 0) : -speed : speed];
if (rpm)
{
myStepper.setSpeed(rpm);
myStepper.setSpeed((speed < 0) ? -STEPS : STEPS)
}
}
For this code, you must choose speeds and STEPS value that will give you an acceleration without misssed steps.

Arduino - Receive Multiple Osc Values at ones

I am trying to send from max msp some values. I have no problem in receiving them however, when I try to send multiple values at one I have some troubles. Is there a way to get multiple values?
using the code below I am getting :
> pixelNum: 10 pixelState: 0 pixelNum: 1 pixelState: 0
void loop() {
OSCMessage msgIN;
int size;
if((size = Udp.parsePacket())>0){
while(size--)
msgIN.fill(Udp.read());
if(!msgIN.hasError()){
msgIN.route("/pixelAni",pixelAni);
}
}
}
void pixelAni(OSCMessage &msg, int addrOffset){
int pixelNum = msg.getInt(0);
int pixelState = msg.getInt(1);
Serial.println("pixelNum: ");
Serial.println(pixelNum);
Serial.println("pixelState: ");
Serial.println(pixelState);
pixels[pixelNum].R = 255;
pixels[pixelNum].G = 255;
pixels[pixelNum].B = 255;
ledstrip.show(pixels);
}
So many options! The simplest would be the pack object, when it receives an input to its left most inlet it will output. Since max data flows Top to Bottom and Right to Left, this should gaurantee that you allways output both numbers
You could use the buddy or thresh objects to sync up the messages if you cant guarntee they arrive at exactly the same time

Millis for arduino

I have two data bases that contain temperature data from an arduino...
I want to send the data to these data bases for a minute,
then after of send the data in the another data base, send it to the next data base (LATER OF TEN TIMES OF THE FIRST DATA BASE)
My code below:
int count = 0;
for(int a = 1; a <= 10; a++) {
Cayenne.run();
delay(60000);
count = count + 1;
}
if(count== 10) {
ToPostWStemp();
count = 0;
}
But doesn't send anything, and I don't know how to do.
Many people said me that it's much better use millis() function, but I don't know how code that function on my Arduino.
D.P. The function "Cayenne.run" calls the first function of server, then the
"ToPostWStemp" calls the second last server function.
Thank you!
If I'm understanding the question correctly, it sounds like you want Cayenne.run() to be called once every minute, and ToPostWStemp() to be called once every 10 minutes.
To do that using millis(), you can simply keep track of the last time each function was called, and compare that against the current value of millis(), calling each function only when the elapsed time exceeds the desired interval. Something like this:
unsigned long cayenneTime = 0;
unsigned long postWSTime = 0;
void loop()
{
if (millis() - cayenneTime >= 60000)
{
// Do this every 60 seconds
Cayenne.run();
// Keep track of the last time this code ran, so we know
// when to run it next time
cayenneTime = millis();
}
if (millis() - postWSTime >= 60000 * 10)
{
// Every 10 minutes, do this
ToPostWStemp();
// Keep track of the last time this code ran, so we know
// when to run it next time
postWSTime = millis();
}
// Do other stuff here
}
Note that millis() will overflow and reset to 0 every 4,294,967,295 milliseconds (about 49 days), so if this is to be a long-running program you'll want to take that into account and adjust for it.

Calculating the average of Sensor Data (Capacitive Sensor)

So I am starting to mess around with Capacitive sensors and all because its some pretty cool stuff.
I have followed some tutorials online about how to set it up and use the CapSense library for Arduino and I just had a quick question about this code i wrote here to get the average for that data.
void loop() {
long AvrNum;
int counter = 0;
AvrNum += cs_4_2.capacitiveSensor(30);
counter++;
if (counter = 10) {
long AvrCap = AvrNum/10;
Serial.println(AvrCap);
counter = 0;
}
}
This is my loop statement and in the Serial it seems like its working but the numbers just look suspiciously low to me. I'm using a 10M resistor (brown, black, black, green, brown) and am touching a piece of foil that both the send and receive pins are attached to (electrical tape) and am getting numbers around about 650, give or take 30.
Basically I'm asking if this code looks right and if these numbers make sense...?
The language used in the Arduino environment is really just an unenforced subset of C++ with the main() function hidden inside the framework code supplied by the IDE. Your code is a module that will be compiled and linked to the framework. When the framework starts running it first initializes itself then your module by calling the function setup(). Once initialized, the framework enters an infinite loop, calling your modules function loop() on each iteration.
Your code is using local variables in loop() and expecting that they will hold their values from call to call. While this might happen in practice (and likely does since that part of framework's main() is probably just while(1) loop();), this is invoking the demons of Undefined Behavior. C++ does not make any promises about the value of an uninitialized variable, and even reading it can cause anything to happen. Even apparently working.
To fix this, the accumulator AvrNum and the counter must be stored somewhere other than on loop()'s stack. They could be declared static, or moved to the module outside. Outside is better IMHO, especially in the constrained Arduino environment.
You also need to clear the accumulator after you finish an average. This is the simplest form of an averaging filter, where you sum up fixed length blocks of N samples, and then use that average each Nth sample.
I believe this fragment (untested) will work for you:
long AvrNum;
int counter;
void setup() {
AvrNum = 0;
counter = 0;
}
void loop() {
AvrNum += cs_4_2.capacitiveSensor(30);
counter++;
if (counter == 10) {
long AvrCap = AvrNum/10;
Serial.println(AvrCap);
counter = 0;
AvrNum = 0;
}
}
I provided a setup(), although it is redundant with the C++ language's guarantee that the global variables begin life initialized to 0.
your line if (counter = 10) is invalid. It should be if (counter == 10)
The first sets counter to 10 and will (of course) evaluate to true.
The second tests for counter equal to 10 and will not evaluate to true until counter is, indeed, equal to 10.
Also, kaylum mentions the other problem, no initialization of AvrNum
This is What I ended up coming up with after spending some more time on it. After some manual calc it gets all the data.
long AvrArray [9];
for(int x = 0; x <= 10; x++){
if(x == 10){
long AvrMes = (AvrArray[0] + AvrArray[1] + AvrArray[2] + AvrArray[3] + AvrArray[4] + AvrArray[5] + AvrArray[6] + AvrArray[7] + AvrArray[8] + AvrArray[9]);
long AvrCap = AvrMes/x;
Serial.print("\t");
Serial.println(AvrCap);
x = 0;
}
AvrArray[x] = cs_4_2.capacitiveSensor(30);
Serial.println(AvrArray[x]);
delay(500);

Resources