PLEASE HELP! Need to add a TARE function to program

Hi there,

I have got most of my code ready to go, however I am stuck with the 'TARE' portion of the code.

I want to keep all of the functionality that the code already compiles however I need the ability for the program to TARE the output as soon as it is initiated, independent of the actual position of the 'potentiometer' input.

Any help would be much appreciated!

Thanks

Taylor

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

//Define Parameters and pins
#define MINRPM 10  // in tenths rpm, 10 = 1 rpm
#define MAXRPM 120 // in tenths rpm, 120 = 12 rpm
#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

//Declare variables for the motor pins on 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; // number of steps per full revolution

// support different output devices by using 'conditional compilation'
#define SHOW_OUTPUT 0 // 0-Serial mode, 1-LCD in 4-bit mode, 2-I2C/IIC mode (Just Results)
#define LCDWIDTH 16

//Option 1 parameters
#if SHOW_OUTPUT == 0  // output functions for Serial

//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("Potival: ");Serial.println(potival);
  Serial.print("Average: ");Serial.println(potiAvg);
}

//Option 2 parameters
#elif SHOW_OUTPUT == 1  // output functions for LCD in 4-bit mode

//Include additional libraries and plugin parameters
#include <LiquidCrystal.h>  
LiquidCrystal lcd(8,9,4,5,6,7);

//Display Begin
void displayBegin()
{
  lcd.begin(LCDWIDTH,2);
  lcd.print("Rotations: ");
}

//Display Rotations
void displayRotations(unsigned int steps2go)
{ // round steps2go to full revolutions
  int rotationsLeft= (steps2go+countsperrev/2-1)/countsperrev;
  lcd.setCursor(10,0);
  if (rotationsLeft<10) lcd.print(' ');
  lcd.print(rotationsLeft);
}

//Display Results
void displayResults(int potival, int potiAvg)
{
  char buf[LCDWIDTH+1];
  lcd.clear();
  snprintf(buf,sizeof(buf),"Potival: %d", potival);
  lcd.print(buf);
  Serial.println(buf);
  snprintf(buf,sizeof(buf),"Average: %d", potiAvg);
  lcd.setCursor(0,1);
  lcd.print(buf);
  Serial.println(buf);
}

//Option 3 Parameters
#elif SHOW_OUTPUT == 2  // output functions for your LCD

//Include additional libraries and plugin parameters
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h> 

//Define variables for LCD display
#define I2C_ADDR 0x27
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACKLIGHT_PIN, POSITIVE);
LCD *myLCD = &lcd;

//Display Begin
void displayBegin()
{
  lcd.begin(LCDWIDTH,2);
  lcd.print("Testing");
}

//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)
{
  char buf[LCDWIDTH+1];
  lcd.clear();
  snprintf(buf,sizeof(buf),"Potival: %d", potival);
  lcd.print(buf);
  Serial.println(buf);
  snprintf(buf,sizeof(buf),"Average: %d", potiAvg);
  lcd.setCursor(0,1);
  lcd.print(buf);
  Serial.println(buf);
}
#endif

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));
}

void rpmTask(unsigned long time, int &rpm, int &steptime)
{ // input time in microseconds since cycle started
  // returns rpm and steptime in microseconds
  #define ACCELLTIME 10000000L  // 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
float loadA = 0;  //Calibration weight 1 (grams)
int analogvalA = 103;  //Change this for calibration

float loadB = 2000; //Calibration weight 2 (grams)
int analogvalB = 1023;  //Change this for calibration

float analogValueAverage = 0;

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;
}

void potentiometerTask(int &value)
{
   int analogValue = analogRead(potentiometerPin);
   value = analogToLoad(analogValue);
}  

//Stepper tasks
void stepperTask(int stepTime)
{
  doOneStep(1,stepTime);
}

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;
}

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);
        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(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);
}
void setup()
{
  // Start Serial for debugging
  Serial.begin(9600);
  displayBegin();
  // 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);
}

void loop()
{  
  Serial.println("Press STARTBUTTON to start new cycle");
  while (!startStop()) ; // do nothing until start/stop condition is true
  Serial.println("START");
  unsigned long time=micros();
  doOneCycle(); 
  Serial.print("This cycle took [s]: ");
  Serial.println((micros()-time)/1000000.0);
  Serial.println("FINISHED");
  Serial.println("");
  delay(500); // half second pausing to prevent next start due to possible button bouncing
}

Can you please describe what the tare function should do ?

I searched for the string 'tare' in your code and found no reference.

What 'portion' are you stuck on exactly?

We cant read your mind.

Sorry, I thought I explained myself, my mistake.

I need to add 'TARE' code into what I already have so that when I press the start button or type the serial command 's', the potentiometer tares to zero (no matter what its current position is) and continues to make readings relative to that zero.

Is this possible?

Thanks

Is this possible?

Yes. When the tare action occurs read the pot, get its value and adjust all subsequent readings by applying the value obtained as an offset to the value read.

For instance, if the tare value is 123 and a subsequent reading is 456 then the post will have changed by 456 - 123 units giving 333 as the net change. Note, however, that you need to allow for negative values in cases where the second reading goes below the tare reading.

Well this is the issue, because my potentiometer is actually a load cell and can either go in the negative or positive directions?

OK. Describe what you would like to happen after the tare if the value goes negative.
Maybe flash the LCD or indicate it in some other way ?

Just let it go negative ??

also keep in mind that positive and negative 'directions' aren't the same as absolute values ...

I just want to make the load cell tare at the beginning of the program and then depending on if it deflects in one direction, get a negative number, and if it deflects in the other, a positive number.

not sure how better to explain this?

If negative is OK then something will do.

When the tare action is triggered

tareValue = analogRead(sensorPin);

Then when readings are taken

int currentValue = analogRead(sensorPin) - tareValue;

I find it odd that you could write the code you post but not figure this out ??

Haha, i am definitely no coding expert and can thank a fair few people for their assistance in writing this code :slight_smile:

The only way id know how is to do this

//Measure weight
void potentiometerTask(int &value)
{ 
  int analogValue = analogRead(potentiometerPin);
  int currentValue = analogRead(potentiometerPin) - analogValue; 
  value = analogToLoad(currentValue);
}

But I'm sure thats not right as its only giving me zero at all times, no matter what the potentiometer position.

No wonder it gives you zero. You read the same pin twice and take one reading from the other. The variable that you have named analogValue is what I called tareValue. It needs to be valued when you decide to tare the system and only then. When and how do you want to tare the system ? Once on startup or maybe on demand by pressing a button ?

OK yes I understand. I want it to happen immediately after I initiate either the push button or type the serial command 's'. It should essentially be the first action completed.

So ... Do that?

Haha! How?

I have tried several different things with no luck.

Can you suggest where i should put it? because i can't put it in the setup() or loop() as it will not recognise the command from earlier in the program.

i can't put it in the setup() or loop() as it will not recognise the command from earlier in the program.

I don't understand what you mean by this.

On demand tare example using a switch or pushbutton

const byte tareButton = A1;
const byte analogPin = A0;
byte currentButtonValue = HIGH;
byte previousButtonValue = HIGH;
int tareValue = 0;

void setup()
{
  Serial.begin(115200);
  pinMode(tareButton, INPUT_PULLUP);
  pinMode(analogPin, INPUT);
}

void loop()
{
  currentButtonValue = digitalRead(tareButton);
  if (currentButtonValue != previousButtonValue && currentButtonValue == LOW)  //button has become pressed
  {
    tareValue = analogRead(analogPin);
    Serial.print("Tare value is ");
    Serial.println(tareValue);
  }
  previousButtonValue = currentButtonValue;
  //rest of your code goes here
}