Arduino Project – LED Skull 5: Adding Subroutines/Functions

Nov 06, 2015

 

This code was a great opportunity to introduce the idea of subroutines and returning values from subroutines (functions) with an Arduino Uno.

 

The last stage of this project added an ultrasonic proximity sensor to the Arduino board, and the code did pretty much everything that we wanted it to. This was the perfect starting point to introduce the idea of subroutines or functions.

Starting with the code from the last stage...

 

// BRIGHTNESS
// minimum brightness level for the led
int brightness_minimum = 1;
// maximum brightness level for the led
int brightness_maximum = 75;
// current brightness level for the led
// changes throughout the script but we set the default here
int brightness_current = brightness_minimum;
 
// PAUSES
// pause between each increase in brightness
int pause_between_increase = 12;
// pause between each decrease in brightness
int pause_between_decrease = 64;
// pause at max brightness, before the wink effect
int pause_at_max_before_wink = 2000;
// pause for the wink effect
int pause_for_wink = 100;
// pause at max brightness, after the wink effect
int pause_at_max_after_wink = 600;
 
 
// DISTANCES
// minimum distance before starting led routine (cm)
int led_distance = 30;
 
 
 
#define trigPin 12
#define echoPin 13
 
const int ledPin1 = 9;     // the pin that the LED is attached to
const int ledPin2 = 10;    // the pin that the LED is attached to
 
void setup () {
 
  // LEDS
  pinMode(ledPin1, OUTPUT);  // declare pin 9 to be an output:
  pinMode(ledPin2, OUTPUT);  // declare pin 10 to be an output:
  // set leds to minimum brightness
  analogWrite(ledPin1, brightness_current);
  analogWrite(ledPin2, brightness_current);
 
  // PROXIMITY SENSOR
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
 
}
 
 
 
void loop() {
 
  long duration, distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration / 2) / 29.1;
 
  if (distance < led_distance ) {     // within led proximity limit; start routine
 
    // ramp up led brightness
    for (int brightness_temp = brightness_current; brightness_temp <= brightness_maximum; brightness_temp++) //loop from brightness_minimum to brightness_maximum
    {
      analogWrite(ledPin1, brightness_temp); // set the brightness of pin 9:
      analogWrite(ledPin2, brightness_temp); // set the brightness of pin 10:
      delay(pause_between_increase);
    }
 
    // safely assume current brightness is now maximum brightness
    brightness_current = brightness_maximum;
 
 
    // hold at max brightness before wink
    delay(pause_at_max_before_wink);
 
    // wink (optional)
    if (pause_for_wink > 0) {
      analogWrite(ledPin1, 0); // turn first led off, creating the wink effect:
      delay(pause_for_wink);
      analogWrite(ledPin1, brightness_maximum); // restore first led to max brightness, ending the wink effect:
    }
 
    long duration, distance;
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration / 2) / 29.1;
 
    if (distance > led_distance ) {
 
      // ramp down led brightness
      for (int brightness_current = brightness_maximum; brightness_current >= brightness_minimum; brightness_current--) //loop from brightness_maximum down to brightness_minimum
      {
        analogWrite(ledPin1, brightness_current); // set the brightness of pin 9:
        analogWrite(ledPin2, brightness_current); // set the brightness of pin 10:
        delay(pause_between_decrease);
      }
 
      // safely assume current brightness is now minimum brightness
      brightness_current = brightness_minimum;
 
    }
 
  }
 
 
  else {
 
    // outside proximity limit; do not start routine
    // we're just going to loop back and test proximity again, but we pause briefly to conserve power
    // there is no need to use power checking proxinity continuously
    delay(100);
 
  }
 
}

 

... the first step was to move the last few lines, the pause between pings, into a subroutine or function. There isn't really any compelling reason to do this – there aren't very many lines (and three quarters of them are comments), and they aren't repeated anywhere. It was whoever an easy place to start.

The guts of this block...

 

else {

    // outside proximity limit; do not start routine
    // we're just going to loop back and test proximity again, but we pause briefly to conserve power
    // there is no need to use power checking proxinity continuously
    delay(100);

}

 

... get moved to their own function at the bottom, like this...

 

// pause between pings
void pause_between_pings() {
    // outside proximity limit; do not start routine
    // we're just going to loop back and test proximity again, but we pause briefly to conserve power
    // there is no need to use power checking proxinity continuously
    delay(100);
}

 

... and the original block is replaced with a call to the new function that looks like this:

 

else {

    pause_between_pings();

}

 

Afterwards we verified the sketch and uploaded it to the Uno, confirming that there were no changes on functionality.

It was then up to the kids to make a similar change to the wink routine. moving it to a function that is called by the main routine. Again – very in the IDE and confirm visually.

The next step was to move a larger chunk of code. This was more beneficial to the overall code – moving several steps required to do something relatively simple (ramp up the LED brightness) makes it much easier to understand the heart of the code at a glance.

Again we took the guts of the ramp routine...

 

// ramp up led brightness
for (int brightness_temp = brightness_current; brightness_temp <= brightness_maximum; brightness_temp++) //loop from brightness_minimum to brightness_maximum
{
    analogWrite(ledPin1, brightness_temp); // set the brightness of pin 9:
    analogWrite(ledPin2, brightness_temp); // set the brightness of pin 10:
    delay(pause_between_increase);
}

// safely assume current brightness is now maximum brightness
brightness_current = brightness_maximum;


// hold at max brightness before wink
delay(pause_at_max_before_wink);

 

... and moved it to a function at the bottom like this...

 


// ramp up
void rampup() {
    for (int brightness_temp = brightness_current; brightness_temp <= brightness_maximum; brightness_temp++) //loop from brightness_minimum to     brightness_maximum
    {
        analogWrite(ledPin1, brightness_temp); // set the brightness of pin 9:
        analogWrite(ledPin2, brightness_temp); // set the brightness of pin 10:
        delay(pause_between_increase);
    }

    // safely assume current brightness is now maximum brightness
    brightness_current = brightness_maximum;

    // hold at max brightness before wink
    delay(pause_at_max_before_wink);

    }

 

... and the original block was replaced with a call to the new function that looks like this:

 

if (distance < led_distance ) { // within led proximity limit; start routine

    // ramp up led brightness
   rampup();

 

It was then up to the kids to do the same thing with the ramp down code – they moved it to a function. The code now looked like this:

 

// BRIGHTNESS
// minimum brightness level for the led
int brightness_minimum = 1;
// maximum brightness level for the led
int brightness_maximum = 75;
// current brightness level for the led
// changes throughout the script but we set the default here
int brightness_current = brightness_minimum;
 
// PAUSES
// pause between each increase in brightness
int pause_between_increase = 12;
// pause between each decrease in brightness
int pause_between_decrease = 64;
// pause at max brightness, before the wink effect
int pause_at_max_before_wink = 2000;
// pause for the wink effect
int pause_for_wink = 100;
// pause at max brightness, after the wink effect
int pause_at_max_after_wink = 200;
 
// DISTANCES
// minimum distance before starting led routine (cm)
int led_distance = 30;
 
 
 
#define trigPin 12
#define echoPin 13
 
const int ledPin1 = 9;     // the pin that the LED is attached to
const int ledPin2 = 10;    // the pin that the LED is attached to
 
void setup () {
 
  // LEDS
  pinMode(ledPin1, OUTPUT);  // declare pin 9 to be an output:
  pinMode(ledPin2, OUTPUT);  // declare pin 10 to be an output:
  // set leds to minimum brightness
  analogWrite(ledPin1, brightness_current);
  analogWrite(ledPin2, brightness_current);
 
  // PROXIMITY SENSOR
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
 
}
 
 
 
void loop() {
 
  long duration, distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration / 2) / 29.1;
 
  if (distance < led_distance ) {     // within led proximity limit; start routine
 
    // ramp up led brightness
    rampup();
 
 
    // check for optional wink
    if (pause_for_wink > 0) {
      // wink
      wink();
    }
 
 
    long duration, distance;
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration / 2) / 29.1;
 
    if (distance > led_distance ) {
      // ramp down led brightness
      rampdown();
    }
 
  }
 
 
  else {
 
    pause_between_pings();
 
  }
 
}
 
 
// SUBROUTINES
 
 
// pause between pings
void pause_between_pings() {
  // outside proximity limit; do not start routine
  // we're just going to loop back and test proximity again, but we pause briefly to conserve power
  // there is no need to use power checking proxinity continuously
  delay(100);
}
 
 
 
// wink
void wink() {
  analogWrite(ledPin1, 0); // turn first led off, creating the wink effect:
  delay(pause_for_wink);
  analogWrite(ledPin1, brightness_maximum); // restore first led to max brightness, ending the wink effect:
}
 
 
 
// ramp up
void rampup() {
  for (int brightness_temp = brightness_current; brightness_temp <= brightness_maximum; brightness_temp++) //loop from brightness_minimum to brightness_maximum
  {
    analogWrite(ledPin1, brightness_temp); // set the brightness of pin 9:
    analogWrite(ledPin2, brightness_temp); // set the brightness of pin 10:
    delay(pause_between_increase);
  }
 
  // safely assume current brightness is now maximum brightness
  brightness_current = brightness_maximum;
 
  // hold at max brightness before wink
  delay(pause_at_max_before_wink);
 
}
 
 
 
// ramp down
void rampdown() {
  for (int brightness_current = brightness_maximum; brightness_current >= brightness_minimum; brightness_current--) //loop from brightness_maximum down to brightness_minimum
  {
    analogWrite(ledPin1, brightness_current); // set the brightness of pin 9:
    analogWrite(ledPin2, brightness_current); // set the brightness of pin 10:
    delay(pause_between_decrease);
  }
 
  // safely assume current brightness is now minimum brightness
  brightness_current = brightness_minimum;
 
}

 

So far it had been easy, in that none of the functions had to return a value – they were just called and did their thing. But the routine that would benefit the most from being moved t a function would have to return a value.

This was the code relating to the ping. Of all the code so far it was actually the best candidate to become a function, because it is both lengthy and repeated, but we held off until now because the return part also made it the most complicated.

Here we moved the guts of the ping routine...

 


   long duration, distance;
   digitalWrite(trigPin, LOW);
   delayMicroseconds(2);
   digitalWrite(trigPin, HIGH);
   delayMicroseconds(10);
   digitalWrite(trigPin, LOW);
   duration = pulseIn(echoPin, HIGH);
   distance = (duration / 2) / 29.1;

 

...into a subroutine like this...

 

// get distance
int get_distance() {
   long duration, distance;
   digitalWrite(trigPin, LOW);
   delayMicroseconds(2);
   digitalWrite(trigPin, HIGH);
   delayMicroseconds(10);
   digitalWrite(trigPin, LOW);
   duration = pulseIn(echoPin, HIGH);
   distance = (duration / 2) / 29.1;
   return distance;
}

 

... and then called it from the original code like this:

 

// check distance
if ( get_distance() < led_distance )

 

The critical part of the subroutine is that last line that reads "return distance", which tells it to "return" the calculated distance value back to the call.

Of the five functions we introduced this is the most helpful, and feels most like a function. When you call the function get_distance, it returns the calculated distance. It makes that part of calling code so much easier to understand – if the distance is less than the minimum difference – it really illustrates the value of functions.

After all these changes the code looks like this.

 

// BRIGHTNESS
// minimum brightness level for the led
int brightness_minimum = 1;
// maximum brightness level for the led
int brightness_maximum = 75;
// current brightness level for the led
// changes throughout the script but we set the default here
int brightness_current = brightness_minimum;
 
// PAUSES
// pause between each increase in brightness
int pause_between_increase = 12;
// pause between each decrease in brightness
int pause_between_decrease = 64;
// pause at max brightness, before the wink effect
int pause_at_max_before_wink = 2000;
// pause for the wink effect
int pause_for_wink = 100;
// pause at max brightness, after the wink effect
int pause_at_max_after_wink = 200;
 
// DISTANCES
// minimum distance before starting led routine (cm)
int led_distance = 30;
 
 
 
#define trigPin 12
#define echoPin 13
 
const int ledPin1 = 9;     // the pin that the LED is attached to
const int ledPin2 = 10;    // the pin that the LED is attached to
 
void setup () {
 
  // LEDS
  pinMode(ledPin1, OUTPUT);  // declare pin 9 to be an output:
  pinMode(ledPin2, OUTPUT);  // declare pin 10 to be an output:
  // set leds to minimum brightness
  analogWrite(ledPin1, brightness_current);
  analogWrite(ledPin2, brightness_current);
 
  // PROXIMITY SENSOR
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
 
}
 
 
 
void loop() {
 
  // check distance
  if (get_distance() < led_distance ) {
    // within led proximity limit; ramp up led brightness
    rampup();
 
    // check for optional wink
    if (pause_for_wink > 0) {
      // wink
      wink();
    }
 
    // check distance
    if (get_distance() > led_distance ) {
      // outside led proximity limit; ramp down led brightness
      rampdown();
    }
 
  }
 
  else {
    pause_between_pings();
  }
 
}
 
 
// SUBROUTINES
 
 
// pause between pings
void pause_between_pings() {
  // outside proximity limit; do not start routine
  // we're just going to loop back and test proximity again, but we pause briefly to conserve power
  // there is no need to use power checking proxinity continuously
  delay(100);
}
 
 
// wink
void wink() {
  analogWrite(ledPin1, 0); // turn first led off, creating the wink effect:
  delay(pause_for_wink);
  analogWrite(ledPin1, brightness_maximum); // restore first led to max brightness, ending the wink effect:
}
 
 
// ramp up
void rampup() {
  for (int brightness_temp = brightness_current; brightness_temp <= brightness_maximum; brightness_temp++) //loop from brightness_minimum to brightness_maximum
  {
    analogWrite(ledPin1, brightness_temp); // set the brightness of pin 9:
    analogWrite(ledPin2, brightness_temp); // set the brightness of pin 10:
    delay(pause_between_increase);
  }
 
  // safely assume current brightness is now maximum brightness
  brightness_current = brightness_maximum;
 
  // hold at max brightness before wink
  delay(pause_at_max_before_wink);
 
}
 
 
// ramp down
void rampdown() {
  for (int brightness_current = brightness_maximum; brightness_current >= brightness_minimum; brightness_current--) //loop from brightness_maximum down to brightness_minimum
  {
    analogWrite(ledPin1, brightness_current); // set the brightness of pin 9:
    analogWrite(ledPin2, brightness_current); // set the brightness of pin 10:
    delay(pause_between_decrease);
  }
 
  // safely assume current brightness is now minimum brightness
  brightness_current = brightness_minimum;
 
}
 
 
// get distance
int get_distance() {
  long duration, distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration / 2) / 29.1;
  return distance;
}

 

It's much easier to quickly read and understand the main goal of the code (see below), by moving so much of it into functions it's easy to process it at a glance.

 

void loop() {

    // check distance
   if (get_distance() < led_distance ) {
      // within led proximity limit; ramp up led brightness
      rampup();

      // check for optional wink
      if (pause_for_wink > 0) {
         // wink
         wink();
      }

   // check distance
   if (get_distance() > led_distance ) {
      // outside led proximity limit; ramp down led brightness
      rampdown();
   }

}

   else {
      pause_between_pings();
   }

}

 

Thanks to the helpful www.syntaxhighlight.com source code beautifier/syntax highlighter.

 

 



Tags: Arduino

Related Content

Fire Breathing Arduino Pumpkin With IR Remote Control

A small change to another fire-breathing Arduino pumpkin replaces a proximity sensor with an small IR remote control.

Arduino Project – LED Skull 4: Add HC-SR04 Ultrasonic Module

The next step in creating a spookier skull was to add an HC-SR04 ultrasonic module that would fire the LEDs based on proximity to a viewer.

Arduino Project – LED Skull Eyes 3: Adding a Second Eye

The next step involves adding a second "eye" (LED), independent of the first, to maintain the wink effect.

Arduino Project – LED Skull Eyes 2: Wink Effect

More variables and the addition of a "wink" effect to make the LED skull eyes seem more animated.

Arduino Project – LED Skull Eyes 1

The first part of this project (adding LED "eyes" to a skull, driven by an Arduino UNO) was about teaching code optimization, abstraction and commenting.

Arduino IDE "'lt' was not declared in this scope" Error

Be aware of a small encoding issue and the resulting "lt/gt was not declared in this scope" error when pasting into the excellent Arduino IDE for Mac.

Category List


Tag List


Tag Cloud



Archive