writing your own functions?

In reading through some of the questions and answers Pixelk stated in another thread

it seems more efficient to split my main function ( MenuUpdate ) into many small functions

and since I am new to C the only understanding I have is that a function is "simular" to a subroutine but I would like to know how they differ and HOW to write your own functions. The only programming I have done prior to getting my Arduino Uno is in BASIC and I had a small collection of subroutines that I could just paste together to "get the job done"

While playing with LEDs I wrote the following code:

if (lastLED == 13)
      lastLED = 10;
  else lastLED = lastLED +1;

but COULD it have been something like:

void whichLED ( ) {
  if (nextLED == 13) {
        nextLED = 10;
        }
       else {
       lastLED = lastLED +1;
       }

And do i need all the { } brackets?

Yes, you could make a function to contain that logic, if lastLED and nextLED are global scope.
Efficient is probably a matter of some discussion. But it is simpler to deal with if your program is modular and functions is a way to group re-usable parts together to make things more modular, easier to understand and easier to maintain.

Something like this:

char nextLED = 0;
char lastLED = 0;

void whichLED(void)
{
  if (nextLED == 13)
       nextLED = 10;
   else
       lastLED = lastLED +1;
}

loop()
{
      :

   whichLED();
 
      :
}

Your example left off the closing brace of the function.
I've added "void" to the argument list, just to be pedantic.
You don't need the braces for the if/else, as long as there is only one statement inside. It's often safer to keep them though.

dmac257:
In reading through some of the questions and answers Pixelk stated in another thread

it seems more efficient to split my main function ( MenuUpdate ) into many small functions

and since I am new to C the only understanding I have is that a function is "simular" to a subroutine but I would like to know how they differ and HOW to write your own functions. The only programming I have done prior to getting my Arduino Uno is in BASIC and I had a small collection of subroutines that I could just paste together to "get the job done"

While playing with LEDs I wrote the following code:

if (lastLED == 13)

lastLED = 10;
  else lastLED = lastLED +1;




but COULD it have been something like:


void whichLED ( ) {
  if (nextLED == 13) {
        nextLED = 10;
        }
       else {
       lastLED = lastLED +1;
       }




And do i need all the { } brackets?

First off - yes, you could have made a function like that (though you are missing an end bracket), and yes (for certain values of "yes") you need the brackets. Now to explain (this might grow into TL;DR, so bear with me):

First off, a function is defined simply as:

{returntype} {functionname}({parameterlist}) {
  // your code here
}

Where {returntype} is the type of value the function returns, {functionname} is the name of the function, and {parameterlist} is a comma-delimited list of parameters (and their type) to pass into the function.

So - in the case of your function (which I have taken the liberty to clean up so as to align the brackets - this is important for maintenance reasons, BTW):

void whichLED() {
  if (nextLED == 13) {
    nextLED = 10;
  }
  else {
    lastLED = lastLED +1;
  }
}

The {returntype} is "void" (meaning it doesn't return a value), the {functionname} is whichLED, and the {parameterlist} is empty (no parameters passed in). Now, one thing I notice about your function is that there are a couple of variables (nextLED, lastLED) in it that don't seem to have any definition. This brings up the concept of "variable scope"; that is, where a variable is defined, and where it can be used and modified.

In the case of your function, if these variables were defined outside of the function (as is seen in many Ardunio sketches), these variables have what is known as "global scope" - they are, in essence, "global variables", in that any function can update them, and any function can use them. Ordinarily, this is seen as a "bad thing" (tm) in software development, and can lead to all sorts of headaches; however, in a microcontroller environment like the Arduino, global-scope variables sometimes can't be avoided. You should strive, however, to limit their use where you can.

So what other kinds of variables are there besides "global"? Well, in the world of functional programming, there's a second type called "local". These variables are declared and used within a function. They can only be accessed within their originating functional block (ie, that which is enclosed by the brackets), and upon exit from the block, their value is "lost" (there are ways around this using pointers and other bits of arcane knowledge, but that would go beyond the scope of this small tutorial). So, for instance, if you had this function:

int myFunction() {
  int myLocalVar = 5;  
  return (myLocalVar);
}

A call to myFunction() would return "5" as an integer value (notice how I defined the variable locally as an integer type, and how my function return type is integer as well), but if you attempted to use the variable "myLocalVar" outside of the function (and it wasn't defined outside of the function as a separate variable), you would either get an error, or you would get the last value of the variable. I'll attempt to show this:

void main() {
  int myLocalVar = 10;  // myLocalVar = 10
  int x = myFunction();  // x = 5
  x = myLocalVar;  // x = 10
}

int myFunction() {
  int myLocalVar = 5;  
  return (myLocalVar);
}

Even if you defined the same variable as a global, variable definitions inside a function are still local to that function, such as:

int myGlobalVar = 10;  // myGlobalVar = 10

void main() {
  int myLocalVar = myGlobalVar;  // myLocalVar = 10
  int x = myFunction();  // x = 5
  x = myLocalVar;  // x = 10
  x = myGlobalVar; // x = 10
}

int myFunction() {
  int myLocalVar = 5;
  int myGlobalVar = 3; // this looks weird, you would never name a local variable with the name "global" 
  return (myLocalVar);
}

Now - what if you wanted to pass one or more values to the function to operate on them? Simple:

void main() {
  int x = myFunction(3, 2);  // x = 6
}

int myFunction(int myVar1, int myVar2) {
  int myLocalVar = 5;
  return (myLocalVar + myVar1 - myVar2);
}

What if you wanted to return more than one value? This is possible, but not easy, so I won't get into it deeply here - but it basically involves passing into the function the address pointer of a variable, grabbing the value at that address (the value of the variable), manipulating it, then storing it back (this in effect should give you an idea of how to access a variable local to a function outside of it, by returning from the function the address of the local variable). Please note that what I have written, while all possible, should be done very carefully, if at all. On a microcontroller (or any system, for that matter), it can lead to sleepless nights and loss of hair (and sanity).

One last thing (not really - if you really want to understand this, you need to pick up a good book on C programming): Say you want to have a local variable inside a function that retains its value between function calls? Use the "static" keyword:

void main() {
  int x = 0;
  for (int i=0; i < 10; i++) {
    x += myFunction(3);
  }
}

int myFunction(int myVar1) {
  static int myLocalVar;
  myLocalVar += myVar1;
  return (myLocalVar);
}

Oh - I almost forgot; you were asking about whether the brackets were absolutely needed. The short answer is "sometimes". The better answer is "always". For instance this:

if (nextLED == 13) {
  nextLED = 10;
}
else {
  lastLED = lastLED +1;
}

...could be written as this (IIRC):

if (nextLED == 13)
  nextLED = 10;
else
  lastLED = lastLED + 1;

IIRC, if you only have a single statement in a code block (between the brackets), the brackets can be done away with. However, if you have multiple statements, then you need the brackets. Its best just to always use the brackets. Remember also to always line up the brackets in a form which suits you best (as well as have a consistent indentation style); this will aid in future maintenance. There are generally two main styles in use in C - the style I have used above, and the following style:

if (nextLED == 13) 
{
  nextLED = 10;
}
else 
{
  lastLED = lastLED + 1;
}

They each have their proponents and detractors; I personally prefer the style that I have used throughout this post, but you may prefer the more aligned version. One last thing (I promise!) - I have put comments in my code examples which -are not- good examples of coding style; comments in real code should serve to define what is going on with the code, and not simply state what the code is doing (which can be easily seen from reading the code). However, in these examples, I did this so-as to make it clear to someone unfamiliar with what is going on, well, what is going on. Hopefully that wasn't too confusing.

I hope this TL;DR posting helps in some manner...good luck!

:slight_smile:

Thank you to both Gardner and cr0sh for your VERY instructional replies. You both caught the ending bracket being missing but that was a simple mistake in my cut/paste from the code I wrote. I understand what you mean about the readablility aspect of keeping the brackets lined up and I will try to follow this in the future. So my understanding is that if the variables are defined outside of ALL functions it is global and can be changed by any function. If it is defined inside a function (even the "main"), calls outside the defining function don't effect that value when the called function is over. It would seem that very specific variables would usually not be effected but it would allow something like

void function() {
  int x = 0;
  for (int i=0; i < 10; i++) {
    //do something here;
  x += 1;
  }
}

so the counter variable could be used within the function and you don't need to worry about effecting a simular counter outside the function??

as for the brackets again... they don't seem to actually end up as part of the code just something to make sure the compiler does what you really want it to do and make sure you can read what you are actually doing.

I have checked out several books on C and they seem harder because the books are outdated and the only "editor" that I have is the Arduion IDE and it doesn't force you to put all the #include statements at the beginning and much is done in between the coding "I write" and the "coding sent to the compiler" that I know nothing about. Even tutorials online are old and refer to WINAVR and AVRDude as separate things to download or something. But I have only had my Arduino Uno for a week. I will get it down.

Again thanks for the tutorial, it really helped me understand it.

Dmac257

dmac257:
so the counter variable could be used within the function and you don't need to worry about effecting a simular counter outside the function??

Yes. Something else to keep in mind - using a variant of your code:

void function() {
  int i = 7;
  for (int i=0; i < 10; i++) {
    //do something here;
  }
  int x = i; // x = 7 here
}

This is because there are two code blocks (the function, and the for() code block) - and "i" is defined and local to each! So - if you want to know the ending value of the loop, you have to define the variable "globally" to the block, like so:

void function() {
  int i;
  for (i=0; i < 10; i++) {
    //do something here;
  }
  int x = i; // x = 10 here
}

dmac257:
as for the brackets again... they don't seem to actually end up as part of the code just something to make sure the compiler does what you really want it to do and make sure you can read what you are actually doing.

Essentially, yes; same with the need for a semi-colon to indicate "end of line" for singular statements (also needed at the end of a class definition code block - something you learn real quick as you develop classes in C++).

dmac257:
I have checked out several books on C and they seem harder because the books are outdated and the only "editor" that I have is the Arduion IDE and it doesn't force you to put all the #include statements at the beginning and much is done in between the coding "I write" and the "coding sent to the compiler" that I know nothing about. Even tutorials online are old and refer to WINAVR and AVRDude as separate things to download or something. But I have only had my Arduino Uno for a week. I will get it down.

Yeah - it will take some perseverance, plus the way the Arduino system pre-processes a bunch of stuff hides things from you, which can make things more difficult to understand from a "straight C" standpoint. If you really want to learn C/C++, get set up on a Linux box with a full gcc dev system, and get a copy of this book:

http://www.deitel.com/books/cpphtp5/

Another book I had good success with learning C (long time ago) was a book called "Easy Programming With C" from Que:

It's like one of those "24 Hours to C Coding" type books, but was laid out in good fashion; another kind of book I would trust (only because I have ones for Perl and PHP) is the "Visual" programming books from IDG; I don't know if they ever made one for C/C++ (and how you would find it might be an exercise in frustration, as you would have a conflict with Microsoft's Visual C++ product, of course).

If you really want to learn C/C++, get set up on a Linux box with a full gcc dev system

unfortunately I am on a fixed income and I just had to replace a "kick butt gaming" computer with a "what I could afford" computer when the video card fried on the "kick butt" and took out the MB as well. I might be able to cobble together another computer from spare parts but that would be months down the road with no guarantees of a Linux box. Gonna stick with the working machine for a while.

Of course one should always try to learn as much new stuff as possible, C\C++ being one of them.
As windows user and Basic programmer I recently did install ubuntu-linux on one of my boxes and...
am busy learning :slight_smile:

If you do however own an Arduino and already have experience with Basic, you might want to have a look at the Bascom-AVR compiler of http://www.mcselec.com/ as well.

Thing I love most about it is that you can quite easily work with most 8-bit AVR controllers, Attinys and atmegas. In case you might find some cheap old 8051-microcontrollers MC-selec also has a basic compiler for a lot of those.

One thing Arduino hardly mentions are fuses. Bascom allows you to work with 'm, you may even have to, which can be quite tricky. Setting the wrong fuse might for example make it impossible to upload new programs without a special programmer to restore it.

dmac257:

If you really want to learn C/C++, get set up on a Linux box with a full gcc dev system

unfortunately I am on a fixed income and I just had to replace a "kick butt gaming" computer with a "what I could afford" computer when the video card fried on the "kick butt" and took out the MB as well. I might be able to cobble together another computer from spare parts but that would be months down the road with no guarantees of a Linux box. Gonna stick with the working machine for a while.

The nice thing about Linux is that it can be made to work with "what you can afford"; in fact, if you have a spare USB stick lying around of a good size, you could build a USB boot stick using an Ubuntu CD, and work from that (I am not sure how that would effect development speeds and such - compiling might be slow). Look around your neighborhood - collect junk parts; use an older install of Linux, or something suited to small systems (there are tons of distros out there). If you can get a 486 with 8 meg of RAM, you can have a Linux dev system. I once had a form of Linux running on a 386 with 6 meg of RAM (I wouldn't reccommend this for development, though).

I found a 486 (well, maybe a 586?) sitting by the side of the road yesterday, free for the taking, complete with monitor (I didn't take the monitor, though - someone else did, though!). Junk. Dunno what I'll do with it, but somebody threw it away; it would make a great Linux box for learning (heck, with a network card or two, it could make a router, a firewall, or a small server of some sort!). This stuff is just being thrown away, given away, molding away in some closet. If you have the time, look around your neighborhood. Look around business complexes and office areas; ask around. Maybe someplace has some junk they want to get rid of, but don't want to pay someone to take it?

There's always the option of setting up a dual-boot system under Linux with your current box (not to mention running Linux, then setting up a virtual machine inside it to run Windows - or vice-versa using Windows running a VM running Linux). Finally - there's http://www.cygwin.com/

:slight_smile: