Go Down

Topic: Writing a function (Read 4566 times) previous topic - next topic

Karl

I am new to Arduino but pretty keen to learn. However already I have come into some problems. How do you write a Function? I looked at the Arduino Language Reference and find this a bit lite-on for information. The example given is only part of the whole picture I am sure. Where does my Function statement begin - Should it be placed where my variables are declared. Before void setup() { or within void loop() {

Once I have a function, how do I call it and pass variables to it?

I guess what would be nice is to perform an autopsy on a more complete but simple example of a function. Can anyone help?

kg4wsv

You've already written two functions if you successfully compile an arduino sketch; they're called setup() and loop().  These functions return no values (in C they return a void type).  In other languages they might be called procedures, but C doesn't differentiate - they're all functions.

Here's a toy example:

Code: [Select]

int plusone(int x)
{
   return x + 1;
}

void setup()
{
   Serial.begin(9600);
}

void loop()
{
   static int n = 0; // static, so that its value is kept between function calls

   n = plusone(n);
   Serial.println(n, DEC);
   delay(5000);
}


In this example the fucntion plusone() takes a single integer as an argument and returns an integer.

Hope this gets you started.  Look for a good book or online tutorial on learning C, and remember in arduino the function main() is already written for you, and it calls your setup() function once and calls your loop() function once every time through the event loop.

-j

Karl

Many thanks for your help here. I am beginning to understand I think. Using your example I wrote this bit of code for a very simple project. It tests a single button. If it is pressed twice within the given time, it should perform a certain function. If it is outside of this time it should perform a different function. It works! I now understand functions a little better. But I fear my code is over engineered. Is there a smarter/leaner way of doing this and have I used "functions" in the right way? here is the code:


int switchPin = 2;      //  digital input pin for a switch
int yellowLedPin = 3;   //  digital output pin for an LED
int redLedPin = 4;      //  digital output pin for an LED
int flashPin = 13;      // digital output pin for an LED
int switchState = 0;    // the state of the switch not needed at the moment
int timerValue = 2000;    // allowed duration value of time passed since button was pressed
int flashMe = 0;             //why do I need to declare this here as well as within the function?
int switchState1 = 0;
int switchState2 = 0;
//int pin = 7;
unsigned long duration1;
unsigned long duration2;
int durationBetween = 0;

void yellowLedActive(int switchState1) {
 // code contained in a function to turn yellowLedPin on and redLedPin off
 digitalWrite(yellowLedPin, HIGH);    // turn on the yellow LED
 digitalWrite(redLedPin, LOW);       // turn off the red LED
 return;
}

void redLedActive(int switchState2) {
 digitalWrite(yellowLedPin, LOW);   // turn off the yellow LED
 digitalWrite(redLedPin, HIGH);     // turn on the red LED
 digitalWrite(flashPin, LOW);
 duration1 = 0;
 duration2 = 0;
 durationBetween = 0;
 delay(2000);
 return;
}

void flashLedActive(int flashMe) {
 // code contained in a function to turn flashLedPin on and off using code below
 digitalWrite(yellowLedPin, HIGH);
 digitalWrite(flashPin, HIGH);    // turn on the yellow LED
 delay(1000);
 digitalWrite(flashPin, LOW);       // turn off the flash LED
 delay(1000);
 digitalWrite(flashPin, HIGH);    // turn on the flash LED
 delay(1000);
 digitalWrite(flashPin, LOW);       // turn off the flash LED
 delay(1000);
 duration1 = 0;      // reset all the durations
 duration2 = 0;
 durationBetween = 0;
 return;
}

void setup() {
 pinMode(switchPin, INPUT);       // set the switch pin to be an input
 pinMode(yellowLedPin, OUTPUT);   // set the yellow LED pin to be an output
 pinMode(redLedPin, OUTPUT);      // set the red LED pin to be an output
 pinMode(flashPin, OUTPUT);       // set the flashing LED on pin 13 to be an output

}

void loop() {
 duration1 = pulseIn(switchPin, HIGH);  // set duration1 with a time marker when button is pressed
 duration2 = pulseIn(switchPin, HIGH); // set duration2 with a time marker when the button is pressed again
 durationBetween = (duration1 - duration2); // find the how much time between presses - is negative but doesn't seen to matter?
 switchState = digitalRead(switchPin); // not in use at the moment
 if (durationBetween > timerValue) {
   redLedActive(switchState2);
 }
 else {
   flashLedActive(flashMe);

 }
 delay(2000);
 digitalWrite(yellowLedPin, LOW);
 digitalWrite(redLedPin, LOW);
}

libhart

Karl,

What you have is basically pretty good.  There are always other ways to do things, efficiencies to be had, but at this point, don't worry to much about them.  You're not building real-time missle defense systems.  Concentrate on understanding everything, then go for the quick lean code.

I'll answer a couple of your questions below.  All variables have scope, scope is the chunk of code in which the variable is valid.  When you put a variable in the header of your function as something that's passed in (like you did with void flashLedActive(int flashMe)), that variable's scope is *only* that function.  That means that after the function, the variable no longer exists, the code as no idea how to find it.  So the reason you must declare flashMe as a global is that you use the variable in the loop function.  Therefore loop() had to know about it.  Putting it in the header of flashLedActive(int) only allowed that function to know about it.  You actually didn't need to pass it in at all.  Because it's global, every function can see that variable.  So you could have simply not passed in anything and just used the global flashMe inside your function.  But I notice that you're not using the flashMe variable in that function, so I'm wondering why you pass it?  BTW, when you have two variables with the same name, the "closer" scope takes precedence, if that makes sense.  So right now, in the flashLedActive function, you cannot get to the flashMe global variable, because you have a local variable called that same name.

Most coders view global variables as sort of a hack way of getting around having to properly pass variables, passing is viewed as the more proper approach.  But hey, it's your board, do what you want, how you want :)


As far as durationBetween being negative, I'm not sure what the question is there.  See the reference link off the arduino.cc homepage, they have nice documentation on the sizes of the variable types.  int's can hold negative numbers, no problem.

So for efficiency, I'd take a stab at the flashLedActive function, and change it like this.

void flashLedActive()
{
 // code contained in a function to turn flashLedPin on and off using code below
 digitalWrite(yellowLedPin, HIGH);   // turn on the yellow LED
 for (int i=0; i<4; i++)
 {
      digitalWrite(flashPin, !digitalRead(flashPin);
      delay(1000);
 }
 return;
}

First, you don't use anything non-global, so why pass a variable.  The loop is just to make the code shorter and make it much easier to flash it as many times as you want.  The digitalWrite coupled with a ! (not) and digitalRead is just a little trick to flip the signal.  BTW, a good example of scope there.  Because I declare "int i" inside the for statement, the scope of the variable i is that loop.  Once the loop is done, i is gone.  Also, there's no good reason to reset the duration variables, because you're code alwways returns to repeat the loop function, which hard sets those variables to another value based on the button press, so setting them to 0 here is just a wasted command.

Hope this helps.

Karl

Libhart, thank you for your in-depth response. I took up your suggestion and replaced the flashLedActive Function with your version. However, after compiling I got an error - "To many arguments passed ..." Looking at it, we aren't passing anything at all so I tried forcing just one argument - an integer. so my code looks like this:

void flashLedActive(int)  {
 // code contained in a function to turn flashLedPin on and off using code below  
 digitalWrite(yellowLedPin, HIGH);   // turn on the yellow LED  
 for (int i=0; i<4; i++)
 {
   digitalWrite(flashPin, !digitalRead(flashPin));
   delay(1000);
 }
 return;  
}


It compiles correctly! Why did I have to pass it the int argument? Also I now see what you mean about passing flashMe back to the Function - it isn't required since it is declared earlier. My understanding of how to setup a Function is now much clearer thank you. I guess my interest in containing my code in "globally available functions" is to make my code more modular and re-usable. Instead of having to sift through many lines of code replacing Variable Values, I know they are defined once inside a Function. This must make it easier to find bugs as well. So I can't understand why this would be viewed as a hack method of declaring variables unless it uses up more memory, slows things down etc? - Maybe this project isn't the most ideal example of how to illustrate the proper use of Functions? My aim was to discover how I could re-use the one push button to perform more then just a single task. Using Functions seemed the right way to go.

mellis

The problem with using global variables instead of passing function arguments is a loss in flexibility.  For example, if you have a function that flashes an LED and accesses the global variable ledPin to decide which pin to use, the function can only flash that pin.  If instead, it takes an argument, you can pass in different pins depending on which LED you want to flash.  Sure, all of those pin variables might be global and accessible from within the function, but the function will have no way of knowing which pin to flash.  

Karl

Ok now this is all starting to make sense. I think this is why I was confused. There are two ways to setup my Function. The first would be to Globally declare my Function and thereby locking a specific LED  to have the "flashing" action. The downside is I can't re-use the code that controls the "flashing" on say another LED. The second method involves creating a function for the act of flashing an LED but not specifying which particular LED. But this method requires you to setup the Function to accept Arguments. In this case the Argument would be a variable to hold which particular LED I would like to flash - is this correct? If so, how would I setup my function to accept a single argument, say my output pin for example? I would be very grateful for a clear example of this.

kg4wsv

Code: [Select]

#define YELLOW_LED 8
#define RED_LED 9
#define BLUE_LED 10

void flash_led(byte led)
{
   digitalWrite(led, HIGH);
   delay(500);
   digitalWrite(led, LOW);
   delay(500);
}

void setup()
{
   pinMode(YELLOW_LED, OUTPUT);
   pinMode(RED_LED, OUTPUT);
   pinMode(BLUE_LED, OUTPUT);
}

void loop()
{

   flash_led(YELLOW_LED);
   flash_led(RED_LED);
   flash_led(BLUE_LED);
}



This will flash each LED once, in sequence, forever.

-j

Karl

Many thanks kg4wsv. Your simple example has helped me greatly. I tried your method of passing the byte value of the output pin number to the led argument in the function - and it works a treat! Also, reading the Arduino reference, this method of defining fixed values at the beginning of the code (using: #define constantName value) saves on chip memory space!! Not only that, it allows the value to be used as a byte (so long as it is between 0 - 255) all in one hit. I guess this would have been easier if I knew the C Language. I wasn't completely aware until now how close the "Arduino Language" and "C" really are! What areas of the C Language are not supported?

kg4wsv

Quote
What areas of the C Language are not supported?

Arduino isn't really a language, it is a combination of an interactive development environment (IDE) and a set of C and C++ libraries, all designed to simplify AVR programming.  It is built on avrlib, which in turn uses the gcc compiler for the Atmel AVR architecture.  As far as I know 100% of C and most of C++ is supported.

If you are used to programming C or C++ on a machine that has an operating system, the biggest difference you will see is the huge number of libraries you take for granted are not available on the Arduino, because there simply isn't room for them, and there isn't an operating system to lean on.  Many people confuse the C language with common library functions; e.g., printf() is not part of the C language. :)

Sorry I can't recommend a C tutorial, but it's been so long since I learned it that any sources I could remember wouldn't be relavant.

-j

CosineKitty

Most C++ features seem to be supported, with the following caveats:

- I have not tried using templates yet.

- Exceptions are disabled... i.e. no "throw" or "catch" statements will work.

- I had a weird linker problem once trying to use pure virtual class methods.  It was easy to work around by replacing the pure virtual method with a do-nothing virtual method: the sketch linked successfully and the virtual methods were overridden properly at run-time.  See the following for more on this topic:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1167672075

CosineKitty

Follow-up:  I just tried a template function at it works fine:

Code: [Select]

template <typename type>
type Maximum (type a, type b)
{
   return (a > b) ? a : b;
}

void setup()
{
   Serial.begin (9600);
}

void loop()
{
   delay(1000);
   Serial.println (Maximum(3,4));
}

Go Up