Making average data values work.

Hey there.

I have what hopefully is a simple task for the more experienced users on here.

I have written (with a lot of help) a program capable of measuring the deflection of a load cell, amongst other things and need to average a bunch of values by collecting samples and then at the end, dividing the sum of these samples by the number taken. All this is fine, i understand all of that.

My issue seems to come in my code where i use the 'float' in the readings line

void potentiometerTask(int &load)
{ 
  int analogValue = analogRead(potentiometerPin);
  analogValueAverage = 0.90*analogValueAverage + 0.10*analogValue; // running average
  int currentValue = analogValueAverage - tareValue;    //Subtracting initial load cell value for all following test readings
    //int currentValue = analogRead(potentiometerPin) - tareValue;
  
  if(millis() > time + timeBetweenReadings){
  float load = analogToLoad(currentValue);
    Serial.print("load: ");Serial.println(load,4);
    time = millis();
  }
}

As soon as i remove the float from float load = analogToLoad(currentValue);
However this is needed to calculate the values from the 'analogToLoad' script.

float analogToLoad(float analogval) {
  
  float load = mapfloat(analogval, analogvalA, analogvalB, loadA, loadB);
  return load;
}

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

I then compute the average later on.

void doOneCycle()
{
  unsigned int totalSteps=10L*countsperrev; // total = 10 rotations to go
  int rpm= 0;
  int stepTime= 0;
  int potentiometerValue= 0;
  int potentiometerSamples= 0;
  long potentiometerValueSum= 0;
  boolean finished=false;
  unsigned long cycleTime=0;
  unsigned long intervalTime=0;
  int intervalCount=0;
  rpmTask(0, rpm, stepTime); // initial calculation of rpm and stepTime
  displayBegin(); // initial output
  displayRotations(totalSteps); // initial output
  unsigned long lastTime=micros();
  while (!finished)
  {
    long now=micros();
    long diff=now-lastTime;
    lastTime=now;
    cycleTime+=diff;
    intervalTime+=diff;
    if (intervalTime>=INTERVAL)
    {
      intervalTime=0;
      intervalCount++;
      if (intervalCount>=20) // after 20 intervals, update display
      {
        intervalCount=0;
        unsigned long tm=micros();
        displayRotations(totalSteps);
        //Serial.println(micros()-tm);  //Debugging Monitor (Duration of Each process in Arduino)
        Serial.println(potentiometerValue,2);
      }
      else if (intervalCount%4==1) rpmTask(cycleTime, rpm, stepTime);
      else if (intervalCount%4==2 && startStop()) totalSteps=1; // cancelled, prepare for final last step
      else // all other intervals
      {
        potentiometerTask(potentiometerValue); // measure value
        potentiometerValueSum+=potentiometerValue; // add up values
        potentiometerSamples++; // increment number of samples
      }
    }
    stepperTask(stepTime); // step the stepper
    totalSteps--;
    if (totalSteps==0) finished=true;
  }
  int average=potentiometerValueSum/potentiometerSamples;
  displayResults(potentiometerValue, average);
}

Is there a simple work around or solution to this problem, as i keep getting potentiometerValue readings of 0 and therefore an average of zero.

Post your complete code. The problem might have something to do with confusion between local and global variables.

...R

////////////////////////////////////////////////////////////////
//Include Arduino Library
#include <Arduino.h>

////////////////////////////////////////////////////////////////
//Define Parameters and pins
#define MINRPM 10  // in tenths rpm, 10 = 1 rpm (This is the startup RPM)
#define MAXRPM 100 // in tenths rpm, 100 = 10 rpm (This is maximum RPM after acceleration)
#define STARTBUTTONPIN A2
#define potentiometerPin A0

//Define interval for timing  rpm calculation and analogRead
#define INTERVAL 25000L   //  25000 µs = 25 ms = execution time, frequency 40 per second

//Define variables for the motor pins on ULN2003/ULN2004 motor driver
#define MOTOR1 8    // Blue   - 28BYJ48 pin 1, driver IN1
#define MOTOR2 9    // Pink   - 28BYJ48 pin 2, driver IN2
#define MOTOR3 10   // Yellow - 28BYJ48 pin 3, driver IN3
#define MOTOR4 11   // Orange - 28BYJ48 pin 4, driver IN4
// Red    - 28BYJ48 pin 5 (VCC)

////////////////////////////////////////////////////////////////
//Define Motor rotations per single shaft rotation (reduction)
const int countsperrev = 4075.7728395; //  (4076??) number of steps per full revolution of external shaft

//Define tareValue for Tare function
int tareValue = 0;

//////////////////////////////////////////////////
//Setup Script (Beginning of each test, "once off')
void setup()
{
  // Start Serial for debugging
  Serial.begin(9600);
  // activate internal pullup resistor for STARTBUTTONPIN
  pinMode(STARTBUTTONPIN, INPUT_PULLUP);
  // Set stepper pins as OUTPUT
  pinMode(MOTOR1, OUTPUT);
  pinMode(MOTOR2, OUTPUT);
  pinMode(MOTOR3, OUTPUT);
  pinMode(MOTOR4, OUTPUT);

/////////////////////////////////////////////////
//Loop script (Run once per cycle)
void loop()
{
  Serial.println("Happy with the Machine Setup? Type 'S' or press STARTBUTTON to start the Test");
  while (!startStop()) ; // do nothing until start/stop condition is true
  tareValue = analogRead(potentiometerPin);
  Serial.println("Test Started, this will take 60 seconds");
  Serial.print("Tare Value: "); Serial.println(tareValue);
  unsigned long time = micros();
  doOneCycle();
  Serial.print("Cycle Time [s]: ");
  Serial.println((micros() - time) / 1000000.0);
  Serial.println("Test Complete");
  Serial.println("Please Unplug DC Jack While Cleaning The Device");
  Serial.println("");
  delay(500); // half second pausing to prevent next start due to possible button bouncing
}

////////////////////////////////////////
//Display Begin
void displayBegin() {}

//Display Rotations
void displayRotations(unsigned int steps2go)
{ // round steps2go to full revolutions
  int rotationsLeft = (steps2go + countsperrev / 2 - 1) / countsperrev;
  //Serial.print("rotations: ");Serial.println(rotationsLeft);
}

//Display Results
void displayResults(int potival, int potiAvg)
{
  Serial.print("Final Value: "); Serial.println(potival);
  Serial.print("Average Value: "); Serial.println(potiAvg);
  Serial.print("Tare Value: "); Serial.println(tareValue);
}

/////////////////////////////////////////////////////////////////////////////
//Create Parameters for 'OneStep' of Motor
void doOneStep(int8_t motorDirection, int motorSpeed)
{ // step motor one single step into 'motorDirection' -1 or 1
  // if called in very short time, delay action up to desired 'motorSpeed'
  static int8_t currentstep = 0;
  static unsigned long lastSteptime;
  long now = micros();
  long diff = now - lastSteptime;
  if (diff < motorSpeed)
  {
    delayMicroseconds(motorSpeed - diff);
    lastSteptime += motorSpeed;
  }
  else lastSteptime = now;
  currentstep += motorDirection;
  if (currentstep >= 8) currentstep = 0;
  else if (currentstep < 0) currentstep = 7;
  byte stepperLookup[8] = {B00001, B00011, B00010, B00110, B00100, B01100, B01000, B01001};
  digitalWrite(MOTOR1, bitRead(stepperLookup[currentstep], 0));
  digitalWrite(MOTOR2, bitRead(stepperLookup[currentstep], 1));
  digitalWrite(MOTOR3, bitRead(stepperLookup[currentstep], 2));
  digitalWrite(MOTOR4, bitRead(stepperLookup[currentstep], 3));
}

/////////////////////////////////////////////////////////
//Motor Acceleration Parameters
void rpmTask(unsigned long time, int &rpm, int &steptime)
{ // input time in microseconds since cycle started
  // returns rpm and steptime in microseconds
#define ACCELLTIME 1000000L  // microseconds for accelleration from MINRPM to MAXRPM
  if (time >= ACCELLTIME) rpm = MAXRPM;
  else rpm = MINRPM + time * (MAXRPM - MINRPM) / ACCELLTIME;
  steptime = 60000000L * 10 / rpm / countsperrev;
}

//////////////////////////////////////////////////
//Calibration for Load Cell
long time = 0; //
int timeBetweenReadings = 400;

float loadA = 0.000;  //Calibration weight 1 (kg)
int analogvalA = 0;  //Change this for calibration

float loadB = 1.000; //Calibration weight 2 (kg)
int analogvalB = 511;  //Change this for calibration

float analogValueAverage = 0;

///////////////////////////////////////////////////
void potentiometerTask(int &load)
{
  int analogValue = analogRead(potentiometerPin);
  analogValueAverage = 0.90 * analogValueAverage + 0.10 * analogValue; // running average
  int currentValue = analogValueAverage - tareValue;    //Subtracting initial load cell value for all following test readings
  //int currentValue = analogRead(potentiometerPin) - tareValue;

  if (millis() > time + timeBetweenReadings)
  {
    float load = analogToLoad(currentValue);
    Serial.print("load: "); Serial.println(load, 4);
    time = millis();
  }
}


//////////////////////////////////////////////////////
float analogToLoad(float analogval)
{
  float load = mapfloat(analogval, analogvalA, analogvalB, loadA, loadB);
  return load;
}

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


/////////////////////////////////////////////////////////
//Initiating Serial Read Commands for Start/Stop
boolean startStop()
{ // handles start/stop by recognising button press or serial 's' received
  // returns 'true' if start/stop condition is detected
  static byte oldState = false;
  byte result = false;
  // first check serial for small letter 's'
  while (Serial.available())
  {
    if (Serial.read() == 's') result = true;
  }
  byte newState = !digitalRead(STARTBUTTONPIN);
  if (newState != oldState)
  {
    oldState = newState;
    if (newState) result = true;
  }
  return result;
}

//////////////////////////////////////////////////
// Creating 'OneCycle' using all Parameters
void doOneCycle()
{
  unsigned int totalSteps = 10L * countsperrev; // total = 10 rotations to go
  int rpm = 0;
  int stepTime = 0;
  int potentiometerValue = 0;
  int potentiometerSamples = 0;
  long potentiometerValueSum = 0;
  boolean finished = false;
  unsigned long cycleTime = 0;
  unsigned long intervalTime = 0;
  int intervalCount = 0;
  rpmTask(0, rpm, stepTime); // initial calculation of rpm and stepTime
  displayBegin(); // initial output
  displayRotations(totalSteps); // initial output
  unsigned long lastTime = micros();
  while (!finished)
  {
    long now = micros();
    long diff = now - lastTime;
    lastTime = now;
    cycleTime += diff;
    intervalTime += diff;
    if (intervalTime >= INTERVAL)
    {
      intervalTime = 0;
      intervalCount++;
      if (intervalCount >= 20) // after 20 intervals, update display
      {
        intervalCount = 0;
        unsigned long tm = micros();
        displayRotations(totalSteps);
        //Serial.println(micros()-tm);  //Debugging Monitor (Duration of Each process in Arduino)
        Serial.println(potentiometerValue, 2);
      }
      else if (intervalCount % 4 == 1) rpmTask(cycleTime, rpm, stepTime);
      else if (intervalCount % 4 == 2 && startStop()) totalSteps = 1; // cancelled, prepare for final last step
      else // all other intervals
      {
        potentiometerTask(potentiometerValue); // measure value
        potentiometerValueSum += potentiometerValue; // add up values
        potentiometerSamples++; // increment number of samples
      }
    }
    stepperTask(stepTime); // step the stepper
    totalSteps--;
    if (totalSteps == 0) finished = true;
  }
  int average = potentiometerValueSum / potentiometerSamples;
  displayResults(potentiometerValue, average);
}

Thanks for posting the code. However I have not been able to follow the logic through it. Maybe it's because there is a lot more going on than simply averaging some readings. Perhaps you can explain the flow.

Have you tried adding Serial.println(vvv) at various points to see what values the variables have at that stage in the process.

...R

The code as posted does not compile.

setup () lost its closing '}'

stepperTask is missing.

One problem with the potentiometer task is the hiding of the reference to int load,
through the definition 'float load = analogToLoad(currentValue);', which I suppose
the OP is believing to be an assignment to the passed variable.

AverangeForum.ino:133:6: warning: unused parameter 'load' [-Wunused-parameter]

Read the fine (warning) messages.

In your mapfloat routine it would be wise to check x
for being less than in_min or bigger than in_max,
or your routine could return quite surprising results.

  if (x < in_min) {
    return out_min;
  }
  if (x > in_max) {
    return out_max;
  }

No problems Whandall, anything you could suggest with regards to the float definition??
It is funny that you couldn't get it to compile as mine is running on my little arduino nano no problems.
With regards to the map float, what you are saying is that if the value is smaller or larger than the calibrated values, the calibration will no longer be linear??

Robin2. Sure thing, no problems at all.
There certainly is a lot more going on that just reading a load cell ad averaging values.
Essentially what the program does is waits for a start command via a push button or serial command. It then initiates a small stepper motor to complete 10 full revolutions, whilst the ADC converter of the arduino reads the output from the load cell at a specified amount of times per second. At the moment this works just fine, and if i remove the map float routine from the program I can get the averaging of values to work aswell.

This is why the following line is here //int currentValue = analogRead(potentiometerPin) - tareValue; because if i activate this and deactivate all the other map float parameters, the whole system works.

I have narrowed the issue down to what I believe is just the term 'float' before float load = analogToLoad(currentValue);, this is because if i change it to 'int' although the program doesn't operate the map float commands, it now completes averages. If you have 123d circuits (free online) you can set this up for yourself to see exactly what happens. (just attach the centre pin of a variable pot to A0 on the Arduino along with gnd and 5v).

I could be completely off however and this is why i need the help. nothing seems to fix the issue.

Many thanks again guys.

Is it because (int &load) is at the top and float load is at the bottom of this?

How would i fix this if so?

void potentiometerTask(int &load)
{
  int analogValue = analogRead(potentiometerPin);
  analogValueAverage = 0.90 * analogValueAverage + 0.10 * analogValue; // running average
  int currentValue = analogValueAverage - tareValue;    //Subtracting initial load cell value for all following test readings
  //int currentValue = analogRead(potentiometerPin) - tareValue;

  if (millis() > time + timeBetweenReadings)
  {
    float load = analogToLoad(currentValue);
    Serial.print("load: "); Serial.println(load, 4);
    time = millis();
  }
}

Just so you know, this will eventually fail due to millis() rollover:

if (millis() > time + timeBetweenReadings)

It has to be:

if (millis() - time >= timeBetweenReadings)

also variable "time" must be unsigned long.

This should work more as intended.

void potentiometerTask(int &load)
{
  analogValueAverage = 0.90 * analogValueAverage + 0.10 * analogRead(potentiometerPin); // running average
  if (millis() - time >= timeBetweenReadings)
  {
    load = analogToLoad(analogValueAverage - tareValue);
    Serial.print("load: "); Serial.println(load);
    time = millis();
  }
}

That looks much cleaner haha. Thank you. But it still doesnt read anything unless I add float to the beginning of the load line. And if I do, my data averaging still comes up as this at the end.

Final Value: 0
Average Value: 0
Tare Value: 505

Any other ideas? D: D: D:

I've just noticed something weird..

maybe you can explain.

Serial.println(potentiometerValue);
      }
      else if (intervalCount%4==1) rpmTask(cycleTime, rpm, stepTime);
      else if (intervalCount%4==2 && startStop()) totalSteps=1; // cancelled, prepare for final last step
      else // all other intervals
      {
        //potentiometerTask;
        //potentiometerTaskSum+=potentiometerTask;
        //potentiometerSamples++;
        potentiometerTask(potentiometerValue); // measure value
        potentiometerValueSum+=potentiometerValue; // add up values
        potentiometerSamples++; // incr

Within this part of the code, the first line actually reads zero for all values, whereas the map float area has an active reading.. it looks like this at present..

load: -1727.4218
0
load: -743.5992
load: -208.6474
0
load: -71.4814
0

where load is the map float code and the zeros are from Serial.println(potentiometerValue); Now obviously its data averaging off of this reading so how do i make them correspond to the load output??

Taylor_woods:
But it still doesnt read anything unless I add float to the beginning of the load line.

Changing the assignment to the passed variable to an assignment to an extra, local, despite printing unused value renders your routine useless.

A main problem with your logic shows in these lines from doOneCycle()

      else // all other intervals
      {
        potentiometerTask(potentiometerValue);       // measure value
        potentiometerValueSum += potentiometerValue; // add up values
        potentiometerSamples++;                      // increment number of samples
      }

You suppose that potentiometerTask will always set potentiometerValue, don't you?

But it won't.

It will only set it, when the timing condition is met and you have no way of detecting it.

For your running averange you use

  analogValueAverage = 0.90 * analogValueAverage + 0.10 * analogValue; // running average

You get (nearly) the same result if you do all the arithmetics in the integer domain via

  analogValueAverage = (9 * analogValueAverage +  analogValue)/10; // running average

if the avarage should be the same value range as the value, or with less error

  analogValueAverage = 9*analogValueAverage/10 + analogValue; // running average

if the avarage can be in 10th of the units of the value.

Sorry, I am by far no programming guru. Im a mechanical engineer with minimal exposure, so bare with me please.

What are you saying with regards to the 'float' term?
How should I rewrite this so the timing condition is met?
Also what does the analogValueAverage line change achieve?

Nevermind, I think I've interpreted what you've said and got it working.
Thankyou for your help! :slight_smile:

I do have one other challenge, don't know if you're able to help.

Essentially what i want to do is add a part of script in at the very beginning, before the load cell tares and starts reading whereby my stepper motor actually reverses one full revolution. What this will achieve is to unload the load cell of any mass prior to testing.