An approach to writing Arduino code

Code Review

Let’s do an example of code development.

Let’s say that we are reading analog values for ranges and want to make decisions based on ranges to set ranges of values. We start with the following snippet. The `if` statements quickly become wordy and program flow maybe starts to become unclear.¬† So lets look at some ways to unravel this with a subroutine.


reading = analogRead(1);

int state = 0;

if (reading > 0 && reading < 500){ 
state = 1; 
} 

if( reading > 499 && reading < 700) { 
state = 2; 
} 

if ( reading > 699  && reading < 1025) {
 state = 3;
}

You could just write a small procedure¬† to simplify this comparison test series…


bool inRange(int x, int y, int z){

 if (x => y && x <= z) {
      return true;
     } else {
      return false;
     }
}

and then call it with much easier to read process requests… like the following code.


//file inrange.ino

// State 1: Connect analog pin to gnd
// State 2: Connect analog pin to 3.3V
// State 3: connect analog pin to 5V

int reading;
int state = 0;

void setup() {
  // put your setup code here, to run once:
 Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
reading =  analogRead(1);

  if(inRange(reading,0,500))
     state = 1;
  
    if(inRange(reading,500,700))
       state = 2;
    
      if(inRange(reading,700,1024))
         state = 3;
      
    Serial.print("Reading ");
    Serial.println(reading);
    Serial.print("state: ");
    Serial.println(state);
    delay(1000);
}

//-------------------------------------------------
// Notice the use of non-informative variable names
// we should fix this.

int inRange(int x, int y, int z){
   bool val;
   if (x >= y && x <= z) {
      val =  true;
    } else {
      val = false;
    }

    return(val);
  }


See? You really get some better logic flow this way.

New lets go a step further… using space saving `const` or constants.


// File: inrange1.ino

// State 1: Connect analog pin to gnd
// State 2: Connect analog pin to 3.3V
// State 3: connect analog pin to 5V

//=====[ CONSTANTS ]============================================================
//Still a bit wordy here...
const int RED = 0;
const int GREEN = 1;
const int BLUE = 2;

//=====[ PINS ]=================================================================
int anPin = 1;


//=====[ VARIABLES ]============================================================
int reading;
int state = 0;



void setup() {
  Serial.begin(9600);
  // Wait for USB on Leonardo and similar devices
  while (!Serial) {
  }

}

void loop() {
  reading =  analogRead(anPin);
  
  // debugging code...
  Serial.print("Reading ");
  Serial.println(reading);



  if (inRange(reading, 0, 499)) 
    state = RED; 
    
  if (inRange(reading, 500, 699)) 
    state = GREEN;
  
  if (inRange(reading, 700, 1024)) 
    state = BLUE;
    
  ColorState();

  delay(1000);
}

//==============================================================================

int inRange(int passVal, int minVal, int maxVal) {

  if (passVal >= minVal && passVal <= maxVal) {
    return true;
  } else {
    return false;
  }
}
//==============================================================================

void ColorState() {
  Serial.print("State: ");

  switch (state) {
    case RED:
      Serial.println("Red");
      break;
    case GREEN:
      Serial.println("Green");
      break;
    case BLUE:
      Serial.println("Blue");
      break;
    default:
      break;

  }
}

Now we start to see how to break out sections of code into useful re-usable subroutines. So lets take this one step further and refine it into a final version using the `enum` command. Notice how the code becomes even a tiny bit simpler.


// File: inrange3.ino

// State 1: Connect analog pin to gnd
// State 2: Connect analog pin to 3.3V
// State 3: connect analog pin to 5V


enum States {
  RED,
  GREEN,
  BLUE
};

States state = RED;  // use the enumeration and initialize to RED

//=====[ PINS ]=================================================================
int anPin = 1;


//=====[ VARIABLES ]============================================================
int reading;

void setup() {
  Serial.begin(9600);
  // Wait for USB on Leonardo and similar devices
  while (!Serial) {
  }

}

void loop() {
  reading =  analogRead(anPin);

  if (inRange(reading, 0, 499))
    state = RED;

  if (inRange(reading, 500, 699))
    state = GREEN;

  if (inRange(reading, 700, 1024))
    state = BLUE;

  ColorState(state);

  delay(1000);
}

//==============================================================================

int inRange(int passVal, int minVal, int maxVal) {

  if (passVal >= minVal && passVal <= maxVal) {
    return true;
  } else {
    return false;
  }
}

//==============================================================================

void ColorState(int range) {
  Serial.print("Condition: ");

  switch (range) {
    case RED:
      Serial.println(" Red");
      break;
    case GREEN:
      Serial.println(" Green");
      break;
    case BLUE:
      Serial.println(" Blue");
      break;
    default:
      break;

  }
}


So while this is not the best code, it definitely accomplishes the simplification goal in the main process loop.

Food for thought.

Leave a Reply

Your email address will not be published. Required fields are marked *