Advice needed to help 'translate' flowchart to code

Hello everyone, this is my first time posting on any forum so I hope I’m doing things right.

I need some help as to how I should continue my Arduino code while following my flowchart. I am doing this for an internship but I have almost zero prior experience with Arduino or any kind of programming language in general.

The program is meant to control 3 relays by detecting and comparing current values of 3 devices.
I have tried looking up many ‘if else’ and ‘switch’ cases online but I have yet to find any information that can help. Another possible way that looks like it will work is a nested-if but my flowchart will require 2 nested-ifs.

My main question is how do I code the part of the flowchart circled in red, so that after [ K2=1 ], the program will continue to the [ I(L)=I(PV) ] decision?

Here is what I have:

int K1=22;  //relay switch K1
int K2=24;
int K3=26;
byte Power=23;     //Virtual switches
byte Charging=25;
byte Manual=27;
byte GetValues=29;
//Variables that are not defined here are defined in another sketch: 
//current_mA_PV, current_mA_Load, current

void setup() {
    pinMode(K1, OUTPUT);
    pinMode(K2, OUTPUT);
    pinMode(K3, OUTPUT);
    pinMode(Power, INPUT);
    pinMode(Charging, INPUT);
    pinMode(Manual, INPUT);
    pinMode(GetValues, INPUT);
    Serial.begin(9600);
}

void loop() {
    int valPower = digitalRead(Power); //Read state of Power button
    int valCharging = digitalRead(Charging); //Read state of Charging button
    int valManual = digitalRead(Manual); //Read state of Manual mode button
    int valGetValues = digitalRead(GetValues); //Read state of GetValues button
    
  if (valCharging > 0) {  // if the Charging is on, turn on K3 only:
    digitalWrite(K1, LOW);
    digitalWrite(K2, LOW);
    digitalWrite(K3, HIGH);
  } else if (valManual > 0) { //if Manual mode on, reset relay switches to 0, for manual operation.
      digitalWrite(K1, LOW);
      digitalWrite(K2, LOW);
      digitalWrite(K3, LOW);
      } else if (current_mA_PV > 0) { //if I(pv)>0A, turn on K2 only:
          digitalWrite(K2, HIGH);
        } else if (current_mA_Load <= current) {  //if I(Load)<=I(batt), turn on K2 and K3 only.
            digitalWrite(K1, LOW);
            digitalWrite(K2, HIGH);
            digitalWrite(K3, HIGH);
          } else if (current_mA_Load > current) { //if I(Load)>I(batt), turn on K1 only.
              digitalWrite(K1, HIGH);
              digitalWrite(K2, LOW);
              digitalWrite(K3, LOW);
            } else {
              continue; //if all condition above are false, return back to Charging stage.
            }              //rest of the code which I do not know where to insert 
}

Attached is a picture of my flowchart for your reference.
Any suggestions to make the code cleaner or better are welcome! :smiley:
Thanks for all your help!

Be aware that Booleans are only o or 1, meaning false or true.

You have:

boolean Power=23; //Virtual switches
boolean Charging=25;
boolean Manual=27;
boolean GetValues=29;

These will all compile to true with a value of 1.

Please change to:

byte Power=23; //Virtual switches
byte Charging=25;
byte Manual=27;
byte GetValues=29;

Paul

I’m a little confused by your flowchart. Perhaps it’s supposed to be this way but what happens at the dead-end conditions? For example, if you follow the chart from “Is IL <= Ibatt” and you set K1…3 as (0,1,1) but then what? In the code do you do this:

.
.
.
else if (current_mA_Load <= current) {  //if I(Load)<=I(batt), turn on K2 and K3 only.
            digitalWrite(K1, LOW);
            digitalWrite(K2, HIGH);
            digitalWrite(K3, HIGH);
.
.
.

But your flowchart shows more:

...
else if (current_mA_Load <= current) {  //if I(Load)<=I(batt), turn on K2 and K3 only.
            digitalWrite(K1, LOW);
            digitalWrite(K2, HIGH);
            digitalWrite(K3, HIGH);
            while(1);

There is there’s no exit from that data shape. If there’s supposed to be an exit, where would it go?

Same with the ‘Y’ answer to “Charging?”; no exit.

This sort of thing is what state machines were designed to do but unless it’s absolutely intentional, a state machine should be “closed”, meaning every state has at least one entry and one exit and no “hangers”.

You might have a dead-end in the flow if a serious or safety-related fault were found and you just want to safe the system and intentionally do nothing. But generally, for most logic, after you perform some operation on I/O or memory, you should go somewhere afterward.

On a similar note, what happens in the “manual operation” shape? What happens during the manual operation of IN1…3 and how does one get out of that condition?

Paul_KD7HB:
Be aware that Booleans are only o or 1, meaning false or true.

Hello, thanks for your suggestion! My thinking was that since I’m using these 4 variables as just on and off switches, I thought that boolean would be suitable. (i.e. the 4 variables will only have 2 states, 1 and 0)

Blackfin:
I’m a little confused by your flowchart. Perhaps it’s supposed to be this way but what happens at the dead-end conditions? For example, if you follow the chart from “Is IL <= Ibatt” and you set K1…3 as (0,1,1) but then what? In the code do you do this:

There is there’s no exit from that data shape. If there’s supposed to be an exit, where would it go?

Same with the ‘Y’ answer to “Charging?”; no exit.

On a similar note, what happens in the “manual operation” shape? What happens during the manual operation of IN1…3 and how does one get out of that condition?

Hi, thanks for your reply! You’re right, there should be a return path after the process is executed. Thus for all of the parallelograms and the ‘manual operation’ shapes, there should be routed back to just below the Power block. I will update the flowchart attachment.

Here’s one framework you can look at. I didn’t try to compile it because it’s not complete but it might illustrate how one approach to your problem:

const byte K1=22;  //relay switch K1
const byte K2=24;
const byte K3=26;
//
const byte Power=23;     //Virtual switches
const byte Charging=25;
const byte Manual=27;
const byte GetValues=29;    //note: if you want this to truly be an interrupt, you'll have to change pins

//Variables that are not defined here are defined in another sketch:
//current_mA_PV, current_mA_Load, current

void setup() 
{
    pinMode( K1, OUTPUT );
    pinMode( K2, OUTPUT );
    pinMode( K3, OUTPUT );
    pinMode( Power, INPUT );
    pinMode( Charging, INPUT );
    pinMode( Manual, INPUT );
    pinMode( GetValues, INPUT );

    //does this need to be an interrupt?
    attachInterrupt( digitalPinToInterrupt( GetValues ), ISR_GetValues, RISING );
    
    Serial.begin(9600);
    
}//setup

void loop() 
{
    static byte
        stateControl = ST_POWER;

    switch( stateControl )
    {
        case    ST_POWER:
            //Power
            if( digitalRead( Power ) == HIGH )
                stateControl = ST_CHARGING;
                
        break;

        case    ST_CHARGING:
            //Charging on?
            if( digitalRead( Charging ) == LOW )
                //No
                stateControl = ST_MANUAL;
            else
            {
                //Yes
                digitalWrite( K1, LOW );
                digitalWrite( K2, LOW );
                digitalWrite( K3, LOW );
                
                stateControl = ST_CHARGING; //redundant, presented for illustrative purposes
                
            }//else
        
        break;

        case    ST_MANUAL:
            //Manual mode on?
            if( digitalRead( Manual ) == LOW )
                //No
                stateControl = ST_CHECK_CURRS;
            else
            {
                //Yes
                //Manual Operation of IN1, INT2, IN3
                //assumed to return false when "finished" manual operation
                //when done, go back to charging state
                if( !HandleManualInputs() )
                    stateControl = ST_CHARGING;
                
            }//else
            
        break;

        case    ST_CHECK_CURRS:
            //Is Ipv > 0A?
            if( current_mA_PV > 0.0 )
            {
                //Yes
                digitalWrite( K2, HIGH );
                //Is Il = Ipv?
                if( current_mA_Load == current_mA_PV )
                {
                    //Yes
                    digitalWrite( K1, LOW );
                    digitalWrite( K3, LOW );
                }//if
                //No
                //Is Il > Ipv?                
                else if( current_mA_Load > current_mA_PV )
                {
                    //Yes
                    digitalWrite( K1, HIGH );
                    digitalWrite( K3, LOW );                    
                }//else if
                //No
                //Is Il < Ipv?
                else
                {
                    //Yes
                    digitalWrite( K1, HIGH );
                    digitalWrite( K3, LOW );                                        
                    
                }//else

            }//if
            //No
            //Is Il <= Ibatt?
            else if( current_mA_Load <= current_mA_Battery )
            {
                //Yes
                digitalWrite( K1, LOW );
                digitalWrite( K2, HIGH );
                digitalWrite( K3, HIGH );
            }//else if
            //No
            //Is Il > Ibatt?
            else
            {
                //Yes
                digitalWrite( K1, HIGH );
                digitalWrite( K2, LOW );
                digitalWrite( K3, LOW );                
            }//else
            //No

            //Go back to chargin state
            stateControl = ST_CHARGING;
            
        break;                
        
    }//switch

}//loop

bool HandleManualInputs( void )
{
    //"Manual Operation of IN1, IN2 and IN3"
    //...
    
}//HandleManualInputs

void ISR_GetValues( void )
{
    //"Get voltage/current of PV, Gen, Load and Batt"
    //...
    
}//ISR_GetValues

Wow!! Thanks for taking the time to write out all the code!! :smiley:

I think I understood how the switch and if cases work now thanks to your detailed comments!
I still have a few questions if you don't mind~

Blackfin:

    //does this need to be an interrupt?

attachInterrupt( digitalPinToInterrupt( GetValues ), ISR_GetValues, RISING );

Sorry but I don't really get your question here?

Blackfin:

bool HandleManualInputs( void )

{
   //"Manual Operation of IN1, IN2 and IN3"
   //...
 
}//HandleManualInputs

How would I handle the inputs here? Do I just use digitalRead?

void ISR_GetValues( void )
{
    //"Get voltage/current of PV, Gen, Load and Batt"
    Serial.println("              INA219_PV  |   INA219_Load   ");
    Serial.print("Load Voltage:  "); Serial.print(loadvoltage_PV); Serial.print("      | "); 
    Serial.print(loadvoltage_Load); Serial.println(" V");
    Serial.print("Current:       "); Serial.print(current_mA_PV); Serial.print("       | "); 
    Serial.print(current_mA_Load); Serial.println("  mA");
    Serial.print("Power:         "); Serial.print(power_mW_PV); Serial.print("         | "); 
    Serial.print(power_mW_Load);Serial.println("     mW"); 
//etc.
   
}//ISR_GetValues

Is this how I should include the GetValues output here?

I've also gotten some errors after compiling, the main ones being that the ST_() group of variables were not declared.
Thanks for your help again!!

You could go to flow code which will convert your flowchart automatically so-to-speak.

Tankfuzz:
Quote from: Blackfin on Today at 12:04 am

//does this need to be an interrupt?
attachInterrupt( digitalPinToInterrupt( GetValues ), ISR_GetValues, RISING );

Sorry but I don't really get your question here?

Interrupts are normally reserved for capturing/responding to short duration events. A human pushing a button doesn't usually qualify because in that case the button input will be active for eons of processor time. A correctly constructed (no delay()) program scan will easily notice and process such. Now, if the input is a 23nsec pulse an interrupt would be indicated because the pulse could start just after the code checks it and then be gone by the time it's checked again.

dougp:
Interrupts are normally reserved for capturing/responding to short duration events. A human pushing a button doesn't usually qualify because in that case the button input will be active for eons of processor time. A correctly constructed (no delay()) program scan will easily notice and process such. Now, if the input is a 23nsec pulse an interrupt would be indicated because the pulse could start just after the code checks it and then be gone by the time it's checked again.

Hi, are you suggesting I don't use the interrupt function?

Tankfuzz:
Hi, are you suggesting I don't use the interrupt function?

I'm suggesting interrupts should not be used indiscriminately. If your application needs interrupts to work then use interrupts. Most times however, what is needed instead is a different approach in the code. If your code is non-blocking the switches and other inputs can be sensed and acted upon using several things at the same time techniques.

Hi,

I will update the flowchart attachment.

Can I suggest you do not go back to posts and change/update their content.
This causes confusion to anyone reading and trying to use this thread as a guide.

You should have posted your revised flowchart in a new post.

Tom... :slight_smile: