Selecting different inputs/outputs as variables in a state machine

I've written down my project in pseudocode, and identified 20 events for a state machine. However, if I can define some of the sensors and outputs (motors, in this case) as variables, then I can bring the total number of cases down from 20 to 6.

Here's a sample case in the state machine:

case OBJECT_DETECTED: 
{
   Serial.println("object detected");
   analogWrite(siding_track_power_PWM, 90); //set motor speed
   digitalWrite(siding_track_power_IN1,LOW); // rotate backward
   digitalWrite(siding_track_power_IN2,HIGH);
   
   if (track_counter % 2 ==0) {
    LDRValue = map(analogRead(IR_siding_blue), 0, 1023, 0, 255);  //reads blue LDR sensor
   }
   else {
    LDRValue = map(analogRead(IR_siding_green), 0, 1023, 0, 255);  //reads green LDR sensor
   }
   
   if (LDRValue > light_sensitivity) {
      if (previousLDRValue2 < light_sensitivity) {
        state = REACT;
      }
   }   
   previousLDRValue2 = LDRValue;   //save light sensor state 
}

break;

So far, I think I've got the sensor figured out: if the variable track_counter is an even number, use the blue sensor, otherwise use the green sensor). But I'd also like the analogWrite and digitalWrite to work with different motors i.e if track counter is even, digitalwrite(motor1,LOW), else digitalwrite(motor2,LOW). So in this case the three items (siding_track_power_PWM, siding_track_power_IN2, siding_track_power_IN2) could all be replaced with other motor controls if a certain condition is met.

Is there a way to do this without so many "if" statements in one case? Can someone post a quick example of the correct syntax, so I can confirm?

There are two things you can do. If you find yourself doing the same thing with different pins at different times, then write a function that takes which pin to use as an argument. If you find yourself doing different things based on a number, like the state, then you can put things like pin numbers into an array and write the statement one time accessing the array to find the pin number.

Delta_G:
If you find yourself doing the same thing with different pins at different times, then write a function that takes which pin to use as an argument.

Yep, that's the situation I need (i.e in Situation 1, use motor 1 and sensor 1, but in Situation 2 do the same thing but with motor 2 and sensor 2).

So in this scenario, the case in the state machine would call up a separate function, which would determine which pins to use, then return those pins to the case in the state machine. Are there any code examples online of this, so that I can check the syntax?

Hmm, I'm still struggling with this.

I have four cases in my state machine. Case 0 says "if the counter is equal, go to state 1, else go to state 2". States 1 and 2 then proceed to State 3 (not important here).

States 1 and 2 read sensors. The only difference between them is which sensor they read (State 1 uses digital pins 39 and 41, State 2 uses pins 43 and 45). These pins are variables called trigPin_Gray, echoPin_Gray, trigPin_White and echoPin_white.

I think there must be a way to reduce the code so that there is only one state instead of two for the sensors, and a way of choosing which input pins to use for trigPin and echoPin. But I'm not sure how to do it.

Here's the working code:

 case SWITCH_SENSORS: 
     { 
         if (track_counter % 2 == 0) {     //if the track counter is an EVEN number
                state = USE_RIGHT_SENSOR;
                }
         else {                            //if the track counter is an ODD number
               state = USE_LEFT_SENSOR;
              }
     }
  break;
  
 case USE_LEFT_SENSOR:
 {
long duration;
    analogWrite(siding_track_power_PWM,100);
    
    digitalWrite(trigPin_Gray, LOW);  // Turn the trigger off
    delayMicroseconds(2); 
    digitalWrite(trigPin_Gray, HIGH);  // Turn the trigger on
    delayMicroseconds(10); 
    digitalWrite(trigPin_Gray, LOW);  // Turn the trigger off
          
    duration = pulseIn(echoPin_Gray, HIGH); //pulse the echo pin to detect
    distance = (duration/2) / 29.1;  //define the distance in centimetres
    
    if (distance < 7) {  //object detected
       digitalWrite(siding_track_power_IN2,LOW); //stop
       state = PULL_AWAY; //change case
    }
    else {
      digitalWrite(siding_track_power_IN1,LOW); //keep moving backwards
      digitalWrite(siding_track_power_IN2,HIGH);
    }
 }
  break;
  
 case USE_RIGHT_SENSOR:
 {
long duration;
    analogWrite(siding_track_power_PWM,100);
    
    digitalWrite(trigPin_White, LOW);  // Turn the trigger off
    delayMicroseconds(2); 
    digitalWrite(trigPin_White, HIGH);  // Turn the trigger on
    delayMicroseconds(10); 
    digitalWrite(trigPin_White, LOW);  // Turn the trigger off
          
    duration = pulseIn(echoPin_White, HIGH); //pulse the echo pin to detect
    distance = (duration/2) / 29.1;  //define the distance in centimetres
    
    if (distance < 5) {  //object detected
       digitalWrite(siding_track_power_IN2,LOW); //stop
       state = PULL_AWAY;
    }
    else {
      digitalWrite(siding_track_power_IN1,LOW); //keep moving backwards
      digitalWrite(siding_track_power_IN2,HIGH);
    }
 }
  break;

I'm guessing (which is why you should post a complete program) this bit

 case SWITCH_SENSORS:
     {
         if (track_counter % 2 == 0) {     //if the track counter is an EVEN number
                state = USE_RIGHT_SENSOR;
                }
         else {                            //if the track counter is an ODD number
               state = USE_LEFT_SENSOR;
              }
     }
  break;

case USE_LEFT_SENSOR:
was preceded by switch state

It looks to me as if you are trying to force things into a single switch-case series where it would be better (and clearer) if you dealt with left/right sensors in a different series of tests (whether switch/case or if/else).

It would help if you could write down the list of various states without any code to obscure them.

Wouldn't it be easier to have a function for the sensors which can be given (a) paramter(s) to signify what sensors are to be used. I do that so that I only have one piece of code in which to make mistakes, rather than two.

...R
Planning and Implementing a Program

I didn't want to post the entire code, as in its current format it's 20 cases (i.e steps in the sequence). If I can work out this issue then I can bring that number down to 6 or 7.

I'm a bit of a beginner at all this, but I've managed to write a function now that determines which sensor to use, along the lines of:

int trigPin = 0;
int echoPin=0;
if (track_counter % 2 ==0) {
trigPin = 39;
echoPin=41; }
else {
trigPin= 43;
echoPin=45;
}

I just need a syntax example of how to put that in a separate function, and send the trigPin and echoPin values back to the state machine.

and send the trigPin and echoPin values back to the state machine.

You can only return 1 value from a function but if you make the variables global you do not need to pass them back.

Grrrmachine:
I just need a syntax example of how to put that in a separate function, and send the trigPin and echoPin values back to the state machine.

Have you studied the link I gave you ?

...R

Sorry Robin, I thought that link was just your signature. I'll read through it ASAP.

HeliBob, thanks for the tip.

UKHeliBob:
You can only return 1 value from a function but if you make the variables global you do not need to pass them back.

Not true - use a struct:

Also - you can pass variables by reference (instead of by value) - so that when they are updated, on exit from the function they remain updated. This isn't the same thing as "returning a variable", but it can be useful - there's a comment in the above stackoverflow thread that details this, too.