HOME > > ARDUINO LANGUAGE COURSE  t.o.c.      Delicious Bookmark this on Delicious Recommend to StumbleUpon

Winking Two LEDs... Properly

(I.e. with well written code)

* This is one page in a series of sequenced tutorials which, together, aim to show you "everything" about the Arduino's programming language.
* There is a page for you with more information about the project in general, and the way these pages are organized.
* Please visit my page with hints for power browsing sometime.

------------ This page, and the software it references, ©TK Boyd, 1/2010.

Good programs. Bad programs.

A good program is one that works, yes? Certainly a program that doesn't is bad... but "works" isn't the only criterion for whether a program is good.

A program is "good" if, not only does it work, but when competent programmers look at the sourcecode, it is easy for them to see what is going on. A good program is also one that can be modified easily.... without things becoming so confused that problems creep in and without programmers painting themselves into corners.

So let's do it!

A huge step towards making a program "good" is to add "comments", which are also known as "remarks", which programmers usually call "rems".

A rem is something put into the sourcecode that means nothing to the compiler. The compiler ignores all rems. That doesn't mean they are worthless!

In a moment, I will show you our earlier program, enhanced with rems. First, as I used the term a moment ago....

"What is a compiler?"

A compiler is a computer program that takes more-or-less human-readable material, the sourcecode, e.g......

     void setup()
        {
            pinMode(12,OUTPUT);...

.... etc, and turns it into what the computer needs to do the job for which the sourcecode was written.

In the case of the Arduino, the good people behind the open source project have incorporated the compiler within an integrated development environment, or "IDE". (Wikipedia's article on IDEs will tell you more, if interested.) All in one, free!, package, we get...>/p>

Forgive a little joke: All we need now is an IDE which would read our minds, so that our sourcecode would do what we MEANT it to do.

Sadly, at the present state of our technology, computers just do what we ACTUALLY said for them to do, irregardless of what we meant to say. Sigh. So for now we had better write good soucecode, so that bugs have no place to hid, and altering the sourcecode is easy. (By the way, for creating Windows programs, the Delphi IDE is well worth seeking out. Version 4 is more than adequate for many, many tasks.)

Putting it into practice...

Here is our program again, with some rems inserted.

/*FEAa1CrudeWink
ver 27 Dec 09

Requires 2 LEDs
*/


//The following function, "setup", must always be present
void setup()
{
   pinMode(12,OUTPUT);//one LED connected here
   pinMode(13,OUTPUT);//other LED connected here
}

void loop()//This function must always be present
{
   digitalWrite(12,LOW);
   digitalWrite(13,HIGH);
   delay(400);//To give human time to see first state
   //delay units: milliseconds
   digitalWrite(12,HIGH);
   digitalWrite(13,LOW);
   delay(400);//To give human time to see second state
}

Notice that there are two ways to do rems?

From the top:

As soon as the compiler sees "/*" it "says" "Oh! The humans are going to type some comments here. I can just ignore everything until I come to an asterisk (*) followed by a forward slash (/)"

Rems enclosed in "/*" and "*/" can extend over many lines.

In our example, the first rem begins...

FEAa1CrudeWink

... which is the name I used when I saved the sourcecode. My files always have this information at the top. They compile perfectly well without it... but having that information there helps me work with the many, many bits of sourcecode I've written over the years. Remember: Sourcecode is only "good" if it is easy to work with. And if you can't find what you need, it isn't easy.

The second line gives the version of this piece of sourcecode. You don't need to change the version "number" (i.e. the date) every time you add a line to the code. So when DO you change it? Whenever a situation arises which means you might have "copy A" in your "left hand", and "copy B" in your "right hand", and you are wondering "Which is the most advanced version?". So, I change the version "number" as soon as I make changes after creating a backup copy of something on a second disk drive, etc. I also change the version ID if I have created a hard copy, i.e. ink on paper, and subsequently change the sourcecode. What if I do such things several times in a day? The "version" after "27 Dec 09" is "27 Dec 09b", the one after that is "27 Dec 09c", etc. Anything marked, say, "27 Dec 09" is assumed to be the "a" version. I never explicitly use, say, "27 Dec 09a".

Also within the big, multi-line comment at the start of the sourcecode I have included some information on the hardware to be connected to the Arduino for the purposes of this program. Note I have NOT said "LEDs on pins 12 and 13." It is best not to duplicate things, and I say where the LED is connected later. The reason you don't duplicate comments is that it is way too easy to forget to alter BOTH copies if the information later changes.

The next line...

//The following function, "setup", must always be present

... is an example of a single line comment. Whenever the compiler sees "//", it ignores the rest of the line.

You won't, I hope, forever have to include the comment about "setup" being required... but comments "cost nothing". Some are like notes scribbled in a margin to help you remember things. Use them liberally.

The "//" to make the rest of a line a comment does not have to be at the start of the line. The comments after the two "pinMode" statements illustrate this.

Look at the rems other software authors put in their sourcecode. Learn to use rems well.

#define

The Arduino reference guide suggests that #define is not the best way to do what I am about to show you, but I still like it. I will show you "their way" in a little later.

In the program as developed so far, there are a lot of 12s and 13s... all being references to pins the LEDs are connected to. First we do a pinMode statement for each pin, and then we do two pinWrites for each pin (four pinWrites in all).

A moment ago I was speaking about the problems that duplication can lead do.

Let's suppose for a moment that you decide to re-wire the connection of the LEDs, attaching them, say, to pins 6 and 7. It could happen; this isn't just a philosophical exercise.

If you did that, you would need to change EVERY 12 to a 6, and every 13 to a 7. It would be quite easy to miss one. Happily, there is a way around this. And "the way" has the added benefit of replacing "12" and "13" with names which may be more meaningful to a reader of the sourcecode.

Just after the....

Requires 2 LEDs
*/

... we need to add....

#define LED0 12
#define LED1 13

Now... a couple of things arise....

a) Computer people tend to number things from zero. That's why I called the first LED "LED0". Making the next one "LED1"

b) Note that there is no semicolon at the end of the line. This is so unusual, and the error messages that arise are so cryptic, that I generally write....

#define LED0 12//no ; here
#define LED1 13//no ; here

... to remind myself.

Now go through the rest of the sourecode, and replace every "12" with "LED0", every "13" with "LED1".

When the compiler processes "#define LED0 12", it makes a note to itself: "Wherever I see LED0, replace it with 12". We've essentially added a new word to the compiler.

You need to be a little careful using #define because it will do the replace EVERYWHERE.

If, for instance, you inserted....

#define LED  11
#define LED0 12
#define LED1 13

... and later used LED0, expecting to get 12 in its place that wouldn't happen. The "LED0" would turn into 110. The "LED" part would turn to 11, from what was set up in the first #define. You would then get the 0 as it would be "left over" after the LED had been turned to "11". Tedious. But #define, used with care, still works.

Using the two #defines suggested would give rise to....

/*FEAa1CrudeWinkFinal
ver 27 Dec 09

Requires 2 LEDs
*/

#define LED0 12  //no ; here
#define LED1 13  //no ; here

//The following function, "setup", must always be present
void setup()
{
   pinMode(LED0,OUTPUT);//one LED connected here
   pinMode(LED1,OUTPUT);//other LED connected here
}

void loop()//This function must always be present
{
   digitalWrite(LED0,LOW);
   digitalWrite(LED1,HIGH);
   delay(400);//To give human time to see first state
   //delay units: milliseconds
   digitalWrite(LED0,HIGH);
   digitalWrite(LED1,LOW);
   delay(400);//To give human time to see second state
}

I think that is more readable. Instead of digitalWrite(12,HIGH) we have digitalWrite(LED0,HIGH). "LED0" is more easily understood for what it is than "12", don't you think? Yes, you CAN understand everything with 12s and 13s... but this is just a silly LITTLE program. What if remembering "12 is the pin the LED is on" was just one of many details to stay on top of?

Furthermore, doing things this way makes modifying the program much easier. If you want to move that LED to, say, pin 6, you only need to change one line of the program. By making....

#define LED0 12

...into...

#define LED0 6

... you "instantly reprogram" things so that the right things for the LED happen to pin 6 instead of happening to pin 12. You don't need to search through the program looking for 12s to change. In a "real" program, there could well be 12s that did NOT refer to the pin the LED is on. It would be reckless to just change ALL 12s.

In such a simple program, the example is strained... but the principle is valid, and doing such things in bigger programs will often pay dividends.

Can you see the other candidate for a #define?


v


v


..... scroll when done looking for the other candidate.....

v


v


v


Yes! (I hope you found it.) The 400, the delay between states. Up with the first two #defines, I would add.....

#define delayDuration 400 //no ; here

... and then I would replace the two 400s which are the arguments of the delay function with "delayDuration".

A few odds and ends to round out this tutorial...

First odd/end: The "delay" function is quite "costly". It ties up the processor, preventing it from getting on with many other things. There are fancier ways to cause a delay, which "cost" less, but that's a story for another day.

Second odd/end: If the Arduino guide doesn't like #define, how would it suggest we do what we've done to improve the readability and flexibility of our code? The suggestion is that we use the const keyword. It stands for "constant", and is used almost the way we've used #define.

In place of #define LED1 13, we would put....

const byte LED1=13;

Note that the semicolon at the line end is back. Consistency is always good. Once this line has been entered in place of the old #define line, then, by a slightly different mechanism, where ever the compiler finds a "LED1", it uses "13". The mechanism may be different, but our needs are met just as well.

The little "fly in the ointment" is that word "byte" after the "const". the word "byte" tells the compiler that the constant we are giving the name "LED1" is of a certain "type". We will talk more about data types later. For now all you need to know is that for a whole number from 0 to 255 (inclusive) "byte" is a suitable type to specify. For whole numbers from 0 to 65536 (inclusive) use "word" as the data type. So, if you wanted to eschew #define to set delayDuration, you could replace that #define line with....

const word dataDelay=400;

Third odd/end: How the program is laid out on your screen is largely up to you. There would be no (sensible) reason to do it, but if you laid out the program as follows, it would still work. (I've shown just selected portions....)

   digitalWrite(LED0,
   LOW);digitalWrite(LED1,HIGH
   );delay(delayDuration);digitalWrite(LED0,
   HIGH);

While it would be silly to arrange the text like that, it is helpful to use things like blank lines and spaces at the start of a line to make the sections of the program jump out at the reader. Again, the more you look at other people's code, the more you will become adept at laying things out well, by which I mean in a manner that is easy to read.

Last odd/end: A little word about how the Arduino IDE behaves when you go to save your work.

Save behaves NEARLY as you would expect.

As with every Windows program I've ever used, if you ask the IDE to "save" something that you have just created from scratch, you don't get a "save"... you get a "save AS". (Or you can invoke one with File|SaveAs if you want to use a new name for something previously saved.)

When the Save As dialog comes up, you get the usual panels and edit boxes showing where you are, and asking you for a "filename". The Arduino IDE lies. If you are in, say "MyDocs", and ask to "save the file" as, say "MyArdu", you will create a FOLDER, "MyDocs/MyArdu". Within THAT will reside whatever you project requires. In a simple case, that would be the .pde file, also called MyArdu, and there would be a folder called applet with lots of stuff, the stuff needed to create the object code. Don't worry about it... just be happy that the IDE takes care of things for you! The system behaves even more strangely, and not as successfully, if you try to use Save As with a name that is already in use. I suggest you avoid that. Delete folders that you want to overwrite, and then do a Save As with the no-longer-in-use name.




   Search this site or the web      powered by FreeFind

Site search Web search
Site Map    What's New    Search

The search engine is not intelligent. It merely seeks the words you specify. It will not do anything sensible with "What does the 'could not compile' error mean?" It will just return references to pages with "what", "does", "could", "not".... etc.

SPELL your search term properly. When I review search logs, it is amazing how many people ask the engine to search for something meaningless.


Why does this site cause a script to run? I have my web-traffic monitored for me by eXTReMe tracker. They offer a free tracker. If you want to try it, check out their site. And if there are Google ads on the page, they are run with scripts, too.


Click here to return to Arduino COURSE table of contents.
Click here to go to the author's home page.

Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.


Here is how you can contact this page's editor. This page, and the software it references, ©TK Boyd, 1/2010.

Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org