Keep track of how many times a function has been run

I'm trying to run 2 different functions (A and B) sequentially, over and over 3 times and then move onto the next function "C". I've never done anything like this before and can't seem to find any good leads on either Google or the Arduino forums.

(I'm sorry for my bad explanation)

Example:

(Enter main loop)

  • Function A (Completes successfully the 1st time)>
  • Function B (Completes successfully the 1st time)>
  • Function A (Completes successfully the 2nd time)>
  • Function B (Completes successfully the 2nd time)>
  • Function A (Completes successfully the 3rd time)>
  • Function B (Completes successfully the 3rd time)>
    Move onto function "C"

Create a global variable and increment it every time the function starts running?

Increment a counter inside the function?

That'll go out of scope as soon as the function ends. I suspect @midnightsparks is looking for something that will 'remember' the count even if the function is called and exits several times.

1 Like

Yeah, maybe I should have expanded my suggestion like you did :grinning:

1 Like

Well, it could have been declared static, which is what I do in recursive functions to test the depth of calls.

That's not what I was looking for, but that's not a bad idea actually... Thank you.

Soo what if A runs 3 times correctly but B doesn't ?

Anyways, wouldn't a global counter var be an idea ? and use conditions.

acounter = 0;
bcounter = 0;

then in the a and b function run this from the "succes" part of the function
if((acounter < 3) { a++; } runC();
if((bcounter < 3) { b++; } runC();

for c:
if(acounter ==2){ do things then reset counter; acounter = 0;
if(bcounter ==2){ do things then reset counter; bcounter = 0;

is it pretty ? nah would it work ? i think so. i'm probaby overlooking something in your request..

1 Like

@callthedutch Thank you for your feedback. I understand what you're saying. Okay, Ill try to edit something like that into my code. It looks like it would work.

thinking about it, not sure if you even need the if(acounter < 3) part, just increment the counter on success and run C to check it every time a or b succeeded and have c reset the counter (sorry just got out of bed, brain still starting up :stuck_out_tongue: )

@callthedutch This is what I came up with based off your suggestion. Does it kind of look like what you were describing?

bool Function_"A"_Complete = false;
bool Function_"B"_Complete = false;

int Function_"A"_Count = 0;
int Function_"B"_Count = 0;

void setup
{
}

void loop()
{
  Function_"A"();
  if (Function_"A"_Complete = true);
  {
    Function_"A"_Count++;
  }
  Function_"B"();
  if (Function_"B"_Complete = true);
  {
Function_"B"_Count++:
  }
  if ((Function_"A"_Count >= 3) && (Function_"B"_Count >= 3));
  {
    Function_"C"();  //Reset flags and counters, etc.
  }
}

Can you expand on the difference between "how many times a function has been run" and "Completes successfully".

Completes successfully could mean it returns a flag indicating that the internal operations of the function were successful - opening a file on an SD card for example.

Which were you wanting to keep track of?

And I want to know why you need/want to do this in the first place.

It sounds like you are aiming at something you haven’t revealed, we can’t see.

There may be a tots different way to accomplish your larger goal.

a7

in proper script/syntax, basicly yes.
There are a few ways to implement the "theory", depends on the situation :slight_smile: (don't forget to reset the boolean flags for function A and B :wink: )

// UNTESTED
void loop( ) {
    byte passA = 0; 
    byte passb = 0; 
    byte count = 0; 

    while( count++ < 3 ) {      // tries 3 times only.
        passA += funcA( );      // if no success in 3 tries...
        passB += funcB( );      //  ...a new trial is started.

        if( passA == 3 & passB == 3 ) {
            funcC( );
            break:
        }
    }

    passA = 0;  
    passB = 0;  
    count = 0;  
}

byte funcA( ) {
    if( /* success */ ) 
        return 1;
    else
        return 0;
}

byte funcB( ) {
    if( /* success */ ) 
        return 1;
    else
        return 0;
}

void funcC( ) {
    /* SUCCESS */
}
1 Like

THANK YOU guys for all your help and advice, I appreciate it!

@alto777 - I'm trying to code a "Finite State Machine" for a prototype I'm working on.
I've already tried using Yakindu (software specifically designed for creating "FSM's") to help me with the control structure of my code. Unfortunately, it's not really user friendly; the learning curve is pretty sharp.
This is essentially what I'm trying to accomplish.

@markd833 I was asking how to keep track of how many times a function (successfully) has run because some of them will need to be repeated over and over until a "complete" flag would indicate otherwise.
I like your SD card idea though to keep track of what "state" my machine is in if it's powered down. That would be helpful.

That was my experience as well. I didn't persevere, so it was just a short session with it, but it looked like a tool that would be very useful if you're actually intending to generate code directly from the tool etc. I only used it to model an FSM at a very basic level and ended up doing it in MS Excel instead.

@koraks How did your FSM turn out in Excel? (Just curious to see if there's a better way of making one besides using a 2D flow chart)

Here's my code currently (it verifies good). I'm more concerned with getting the code's "control structure" correct. From there, I can tweak my functions as necessary.
I'm aware that my "void loop" section looks a bit funny. It's easier for me to read this way.

/*
  //HARDWARE LIST
   - Arduino Uno R3 (x1)
   - Servo Motors (MG996R, x2)
   - Parallax Continuious Rotation Servo Motor with Feedback Pin (x1)
   - Adafruit INA219 Current Sensor (x2)
   - Adafruit IR Sensor (x1)
   - 4-Pin LED Module (x4)
*/


//---------------

//LIBRARIES
#include <Wire.h>  //Used for the current monitors
#include <Adafruit_INA219.h>  //Used for the INA219 current sensors
#include <Servo.h>  //Used for Servo's 2 and 3
#include "FeedBackServo.h"  //Used for Servo 1

//CURRENT MONITORS
Adafruit_INA219 ina219_1;  //Instance for Servo1's current monitor
Adafruit_INA219 ina219_3;  //Inmstance for Servo3's current monitor

//PARALLAX SERVO 1
const int FEEDBACK_PIN = 2;  //Define feedback signal pin (Yellow wire)
const int SERVO_PIN = 3;  //Define control pin (White wire)
FeedBackServo Servo_1 = FeedBackServo(FEEDBACK_PIN);  //Servo_1 - Set feedback signal pin number

//SERVO 2 & SERVO 3
Servo Servo_2;  //Instance for the servo motor 2
Servo Servo_3;  //Instance for the servo motor 3

//IR SENSOR
const int IR_Sensor_Pin = 4;  //Defines the IR sensor pin to pin 2

//LEDS
const int RED = 3;  //PWM pin 3
const int GREEN = 5;  //PWM pin 5
const int BLUE = 6;  //PWM pin 6

//VARIABLES
const int Reset_Button_Pin = 8;
int Servo_1_Micro_Position_2_Count = 0;
int Servo_1_Position_1_Count = 0;
int Servo_1_Position_1_Dispensing_Count = 0;
int Servo_1_Position_2_Count = 0;
int Servo_1_Position_3_Count = 0;
int Servo_3_Position = 0;

//STATES
int IR_Sensor_State = HIGH;  //HIGH = The natural "state" of the variable when the beam is unbroken
int Reset_Button_State = HIGH;  //State of the Reset_Button without being pressed

//FLAGS
bool IR_Beam_Broken = false;  //false = The natural "state" of the variable
bool Servo_1_Micro_Adjustments_Complete = false;
bool Servo_1_Micro_Position_2_Complete = false;
bool Servo_1_Position_1_Complete = false;
bool Servo_1_Position_2_Complete = false;
bool Servo_1_Position_3_Complete = false;
bool Servo_2_Forward_Complete = false;
bool Servo_2_Backward_Complete = false;
bool Servo_3_Slowly_Forward_Complete = false;
bool Servo_3_Backward_Complete = false;
bool Dispense_Bowls_Complete = false;
bool Maint_Light_Active_Complete = false;
bool Maint_Light_Inactive_Complete = false;


//---------------

void setup()
{
  //Initialize Serial Monitor
  Serial.begin(9600);  //Initialize the serial monitor

  //Initialize LED's
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  analogWrite(RED, 0);
  analogWrite(GREEN, 0);
  analogWrite(BLUE, 0);

  //Initialize IR Sensor
  pinMode(IR_Sensor_Pin, INPUT);  //Initialize the IR sensor pin as an input
  digitalWrite(IR_Sensor_Pin, HIGH);  //Turn on the internal Arduino pullup resistor to set the sensor HIGH

  //Initialize Current Sensors
  ina219_1.begin();  //Start Servo 1's current module
  ina219_3.begin();  //Start Servo 3's current module
  uint32_t currentFrequency;  //Part of the INA219 library. Not sure what this is for, but if I delete it, the current sensors wont work right

  //Initialize Servo 1
  Servo_1.setServoControl(SERVO_PIN);  //Set Servo1 control pin number
  Servo_1.setKp(1.0);

  //Initialize Servo's 2 and 3
  Servo_2.attach(10);  //Attach servo 2 to PWM pin 10
  Servo_3.attach(11);  //Attach servo 3 to PWM pin 11

  //Servo 3 Backward Setup
  Servo_3_Backward_Setup();

  //Servo 2 Backward Setup
  Servo_2_Backward_Setup();

  //Servo 2 Forward Setup
  Servo_2_Forward_Setup();

  //Servo 1 Position 1 Setup
  Servo_1_Position_1_Setup();
}


//---------------

void loop()
{
  IR_Beam();
  if (IR_Beam_Broken = true);
  {
    Servo_3_Slowly_Forward();
    if (Servo_3_Slowly_Forward_Complete = true);
    {
      Servo_2_Backward();
      if (Servo_2_Backward_Complete = true);
      {
        Servo_1_Micro_Adjustments();
        if (Servo_1_Micro_Position_2_Complete = true);
        {
          Servo_1_Micro_Position_2_Count++;
        }
        if (Servo_1_Position_1_Complete = true)
        {
          Servo_1_Position_1_Count++;
        }
        if ((Servo_1_Micro_Position_2_Count == 3) && (Servo_1_Position_1_Count == 3));
        {
          Servo_1_Micro_Adjustments_Complete = true;
        }
        if (Servo_1_Micro_Adjustments_Complete = true)
        {
          Servo_2_Forward();
          if (Servo_2_Forward_Complete = true)
          {
            Servo_3_Backward();
            if (Servo_3_Backward_Complete = true)
            {
              Dispense_12_Bowls();
              if (Servo_1_Position_2_Complete = true);
              {
                Servo_1_Position_2_Count++;
              }
              if (Servo_1_Position_1_Complete = true);
              {
                Servo_1_Position_1_Dispensing_Count++;
              }
              if ((Servo_1_Position_2_Count == 12) && (Servo_1_Position_1_Count == 12));
              {
                Dispense_Bowls_Complete = true;
              }
              if (Dispense_Bowls_Complete = true)
              {
                Reset_All_Flags();  //End of loop
              }
            }
          }
        }
      }
    }
  }
}


//---------------
//SETUP FUNCTION LIST
void Servo_3_Backward_Setup()
{
  Servo_3.write(0);  //Move Servo 3 backward to 0 degrees
  delay(1500);
  //Create an if statement here to determine if Servo 3 moved backward sucessfully
  Serial.println("Servo 3 Backward Setup completed successfully");
  Serial.println();
}

void Servo_2_Backward_Setup()
{
  Servo_2.write(0);  //Move Servo 2 backward to 0 degrees
  delay(1500);
  Serial.println("Servo 2 Backward Setup completed successfully");
  Serial.println();
}

void Servo_2_Forward_Setup()
{
  Servo_2.write(180);  //Move Servo 2 forward to 180 degrees
  delay(1500);
  Serial.println("Servo 2 Forward Setup completed successfully");
  Serial.println();
}

void Servo_1_Position_1_Setup()
{
  Servo_1.rotate(0, 4);  //Rotate to 0 degrees + - 4 degrees
  Serial.println("Servo_1_Position_1_Setup completed successfully");
  Serial.println();
}


//---------------
//LOOP FUNCTION LIST
void IR_Beam()  //Check the IR beam's "state"
{
  if (IR_Sensor_State == LOW)  //LOW = The IR beam is broken
  {
    Serial.println("The IR beam is broken");
    Serial.println();
    IR_Beam_Broken = true;
  }
  else (IR_Sensor_State == HIGH);  //HIGH = The IR beam is unbroken
  {
    return;  //Would this exit the function keep looping until the "IR_Sensor_State" changes from HIGH to LOW?
  }
}

void Servo_1_Position_1()
{
  Servo_1.rotate(0, 4);  //Rotate to 0 degrees + - 4 degrees
  //Create an if statement here to determine if servo 1 moved sucessfully to position 1
  Serial.println("Servo 1 Position 1 completed successfully");
  Serial.println();
  Servo_1_Position_1_Complete = true;
}

void Servo_1_Micro_Position_2()
{
  Servo_1.rotate(35, 4);  //Rotate to 35 degrees + - 4 degrees
  //Create an if statement to determine if servo 1 moved sucessfully to micro-position 1
  Serial.println("Servo 1 Micro Position 1 has completed successfully");
  Serial.println();
  Servo_1_Micro_Position_2_Complete = true;
}

void Servo_1_Position_2()
{
  Servo_1.rotate(175, 4);  //Rotate to 175 degrees + - 4 degrees
  //Create an if statement here to determine if servo 1 moved sucessfully to position 2
  Serial.println("Servo 1 Position 2 completed successfully");
  Serial.println();
  Servo_1_Position_2_Complete = true;
}

void Servo_1_Position_3()
{
  Servo_1.rotate(220, 4);  //Rotate to 220 degrees + - 4 degrees
  //Create an if statement here to determine if Servo 1 moved sucessfully to position 3
  Serial.println("Servo 1 Position 2 completed successfully");
  Serial.println();
  Servo_1_Position_2_Complete = true;
}

void Servo_2_Forward()
{
  Servo_2.write(180);  //Move Servo 2 forward to 180 degrees
  delay(1500);
  //Create an if statement here to determine if Servo 1 moved sucessfully to position 3
  Serial.println("Servo 2 Forward completed successfully");
  Serial.println();
  Servo_2_Forward_Complete = true;
}

void Servo_2_Partially_Backward()
{
  Servo_2.write(150);  //Move Servo 2 backward 30 degrees
  delay(1500);
  //Create an if statement here to determine if Servo 2 moved successfully
  Serial.println("Servo 2 Forward completed successfully");
  Serial.println();
  Servo_2_Forward_Complete = true;
}

void Servo_2_Backward()
{
  Servo_2.write(0);  //Move Servo 2 backward to 0 degrees
  delay(1500);
  Serial.println("Servo_2_Backward completed successfully");
  Serial.println();
  Servo_2_Backward_Complete = true;
}

void Servo_3_Slowly_Forward()  //Servo 3 slowly goes from 0 degrees to 180 degrees in increments of 1 degree at a time
{
  for (Servo_3_Position = 0; Servo_3_Position <= 180; Servo_3_Position += 1)
  {
    Servo_3.write(Servo_3_Position);  //Move Servo 3 to 'Servo_3_Position'
    float current_mA = 0;  //This is for the current monitor
    current_mA = ina219_3.getCurrent_mA();  //This is for the current monitor
    Serial.print("Servo 3 Forward:       ");
    Serial.print(current_mA);  //Serial print "current_mA"
    Serial.println(" mA");  //Serial print "mA"
    Serial.print("Degree:                ");
    Serial.println(Servo_3_Position);  //Serial print "Servo_3_Position"
    Serial.println();
    delay(15);  //Controls Servo 3's rotational speed from 1 degree to the next. Example: Going from 1 degree to 2 degrees has a 15ms delay in between each degree
    if ((current_mA) >= 120.00 && Servo_3_Position > 15)  //If the current monitor detects a value greater than or equal to 120.00 and has a degree greater than 15. This eliminates the chance of a false flag due to the initial current inrush when Servo_3 starts.
    {
      delay(1);
      Serial.println("Servo 3 Slowly Forward - FLAG #1 - Failed to move forward successfully");
      Serial.println();
      float current_mA = 0;  //This is for the current monitor
      current_mA = ina219_3.getCurrent_mA();  //This is for the current monitor
      Serial.print("Servo 3 Forward:       ");
      Serial.print(current_mA);  //Serial print "current_mA"
      Serial.println(" mA");  //Serial print "mA"
      Serial.print("Degree:                ");
      Serial.println(Servo_3_Position);  //Serial print "Servo_3_Position"
      Serial.println();
      if ((current_mA) >= 120.00)  //If the current monitor detects a value greater than or equal to 120.00 for the 2nd time
      {
        delay(1);
        Serial.println("Servo 3 Slowly Forward - FLAG #2 - Failed to move forward successfully");
        Serial.println();
        float current_mA = 0;  //This is for the current monitor
        current_mA = ina219_3.getCurrent_mA();  //This is for the current monitor
        Serial.print("Servo 3 Forward:       ");
        Serial.print(current_mA);  //Serial print "current_mA"
        Serial.println(" mA");  //Serial print "mA"
        Serial.print("Degree:                ");
        Serial.println(Servo_3_Position);  //Serial print "Servo 3 Position"
        Serial.println();
        if ((current_mA) >= 120.00)  //If the current monitor detects a value greater than or equal to 120.00 for the 3rd time
        {
          Serial.println("Servo 3 Slowly Forward - FLAG #3 - Failed to move forward successfully");
          Serial.println();
          float current_mA = 0;  //This is for the current monitor
          current_mA = ina219_3.getCurrent_mA();  //This is for the current monitor
          Serial.print("Servo 3 Forward:       ");
          Serial.print(current_mA);  //Serial print "current_mA"
          Serial.println(" mA");  //Serial print "mA"
          Serial.print("Degree:                ");
          Serial.println(Servo_3_Position);  //Serial print the variable "Servo_3_Position"
          Serial.println();
          if ((current_mA) >= 120.00)  //If the current monitor detects a value greater than or equal to 120.00 for the 4th time
          {
            Servo_3.detach();  //Disconnect Servo 3 from PWM pin 9
            Blue_LEDs_On();  //Turn on the blue LED's
            Serial.println("Servo 3 Slowly Forward - Flag #4 - Software Reset");
            Serial.println();
            delay(1500);
            Reset_All_Flags();
            Software_Reset();
          }
        }
      }
    }
  }
  Serial.print("////////////////////////////////////");  //Print a dividing line for easy viewing
  Serial.println();
  Serial.println();
  delay(1000);
  Serial.println("Servo 3 Slowly Forward has completed successfully");
  Serial.println();
  Servo_3_Slowly_Forward_Complete = true;
}

void Servo_3_Backward()
{
  Servo_3.write(0);  //Move Servo 3 backward to 0 degrees
  delay(1500);
  //Create an if statement to determine if Servo 3 moved backward sucessfully
  Serial.println("Servo 3 Backward Setup completed successfully");
  Serial.println();
}


//---------------
//MISC. FUNCTIONS
void Servo_1_Micro_Adjustments()
{
  Servo_1_Micro_Position_2();
  Servo_1_Position_1();
}

void Dispense_12_Bowls()
{
  Servo_1_Position_2();
  Servo_1_Position_1();
}

void Green_LEDs_On()
{
  analogWrite(RED, 0);  //Off
  analogWrite(GREEN, 255);  //On - Max value
  analogWrite(BLUE, 0);  //Off
}

void Yellow_LEDs_On()
{
  analogWrite(RED, 255);  //On - Max value
  analogWrite(GREEN, 155);  //On
  analogWrite(BLUE, 0);  //Off
}

void Red_LEDs_On()
{
  analogWrite(RED, 255);  //ON - Max value
  analogWrite(GREEN, 0);  //Off
  analogWrite(BLUE, 0);  //Off
}

void Blue_LEDs_On()
{
  analogWrite(RED, 0);  //Off
  analogWrite(GREEN, 0);  //Off
  analogWrite(BLUE, 255);  //On - Max Value
}

void LEDs_Off()
{
  analogWrite(RED, 0);  //Off
  analogWrite(GREEN, 0);  //Off
  analogWrite(BLUE, 0);  //Off
}

void Maint_Light_Active()
{
  Blue_LEDs_On();
  Servo_2.detach();
  Servo_3.detach();
  Serial.println("The Maintenance Light has been activated");
  Serial.println();
  Maint_Light_Active_Complete = true;
  //Add code to check if the maintenance bypass key has been used. If so, call Maint_Light_Inactive
}

void Maint_Light_Inactive()
{
  //Code to watch if the maint key has been used, if so:
  LEDs_Off();
  Serial.println("The Maintenance Light has been deactivated");
  Serial.println();
  Maint_Light_Inactive_Complete = true;
  Software_Reset();
}

void Reset_Button()
{
  //Add code here to check to see if the reset button has been pressed
}


void Software_Reset()
{
  //Add code here to reset the Arduino via software command
}

void Reset_All_Flags()
{
  bool IR_Beam_Broken = false;  //false = The natural "state" of the variable
  bool Servo_1_Micro_Adjustments_Complete = false;
  bool Servo_1_Micro_Position_2_Complete = false;
  bool Servo_1_Position_1_Complete = false;
  bool Servo_1_Position_2_Complete = false;
  bool Servo_1_Position_3_Complete = false;
  bool Servo_2_Forward_Complete = false;
  bool Servo_2_Backward_Complete = false;
  bool Servo_3_Slowly_Forward_Complete = false;
  bool Servo_3_Backward_Complete = false;
  bool Dispense_Bowls_Complete = false;
  bool Maint_Light_Active_Complete = false;
  bool Maint_Light_Inactive_Complete = false;
}

Fine, but it didn't create pretty pictures or anything. Basically I just made a worksheet with all the distinct states, then another worksheet with a list of routes that defines 'from state', 'to state' and 'action' which basically determines the behavior of the system. I used it to auto-generate arrays and structs so I didn't have to type them by hand. Worked like a charm for the most part; the only annoying parts were the way Excel adds double quotes if you copy-paste stuff to another app and the character limit for the contents of a single cell. Otherwise it was quite convenient. I basically wrote a generic state machine library (actually, a collection of libraries) that perform all fundamental tasks and I could define machine behavior in terms of which outputs to activate at what point etc. by adding lines in the Excel sheet. So none of the actual states, inputs or outputs are hardcoded apart from a few configuration files that contain the auto-generated code from Excel.
Note that the Excel stuff wasn't a substitute for a 2D flow chart, but an intermediary step between the flow chart and the actual C++ code. You could of course take the 'routes' worksheet and turn it into a flowchart pretty easily - or vice versa. That's what Yakindu essentially does as well, but it's a bit bloated if you want to keep your project somewhat basic.

1 Like