HOME  → Other material for programmers → Arduino Tutorial Table of Contents
Delicious.Com Bookmark this on Delicious StumbleUpon.Com Recommend to StumbleUpon

Arduino Programming: More on Variables


This tutorial has a lot of words in it... and the early parts of it may be hard to work your way through. But be of good cheer! Once you get past the early part, you'll find that a lot of the later sections are much less demanding! Do you have the stamina (stubbornness!) to be a programmer? (8200 "words" at the moment. But my editor is probably counting the HTML tags (which you won't be seeing) as well as what we humans call "words". You probably have less than 6000 to read!)

In the previous tutorial, we created and used our first variables. They were of the type "byte", which means they could store numbers. (only whole numbers, and only zero - 255, but that's another story!)

Variables for numbers was a good place to start, but you can store other TYPES of data, and once you know about that, the program we wrote last time can be written more simply. Simple is always good, because it makes your code more clear... helping reveal any mistakes.

While "types of data" makes sense to anyone, to programmers the word "type" means something specific. If a variable has been declared to be of, for example, "type" byte, you get a set of advantages and a set of limitations. While sometimes frustrating, these limitations makes it harder to write bugs in your programs, and makes it easier for the person who writes the compiler to do a good job. (The compiler takes your program and converts it to what the computer needs.)


The next type of data I'm going to show you is Boolean data. A Boolean variable holds either "true" or "false".

In this tutorial, along the way of learning about some things, we are going to create a machine to count to three in binary!

You'll need an Arduino with a switch on an input... a toggle switch would be ideal, but if you're already set up with a push button switch don't change it.

Your Arduino will also need two LEDs. (IF it has 3, and you've understood the tutorial, you could make your machine count to seven Whee!

Some people just don't understand the finer things in life, but if you are reading this I'll assume that you aren't one of them, and press on.

Here's what will happen when you start the program:

The LEDs will be off... ("saying" zero)... until you change the setting on the toggle switch, or press AND HOLD the push button. Then the first LED will come on, saying "1". On the next change of the switch's state, be it due to flipping the toggle switch, or releasing the push button, the first LED will go off, and the other one will go on, saying "2". On the next change of the switch's state, both LEDs will come on, saying "3".

What happens on the NEXT change of the switch's state illustrates nicely an important property of byte-type data. The LEDs go back to both off, saying "zero" again. This is called "roll over". In a computer, if you try to count too high, for example if you try to add 1 to 255 in a byte-type variable, you go back to where you started.

So much for what the program will eventually do.

As an aid to your learning, we are going to build the program in stages. To become a fluent programmer, you not only need to master the language, you also (and it is more important) need to master the art (not science) of building something up in judiciously planned phases. It is quite easy to produce two radically different programs that behave, as far as the user is concerned, the same way. A well built program... beyond the smallest, trivial, examples... will be finished faster, and it will be more easily modified to add new features, or re-configure old ones.

So... on to the first stage! All this will do is wait for that first change of button state, and then turn on the first LED.

Enter the following... see note below it about "It will probably pay..." before copying and pasting.

/*plt1d_1
ver 12 Feb 08
First program for pltd.htm */
//Needs two LEDs, one switch

boolean boItWasOn;
boolean boItIsOn;

#define ledPin0 13 /* (No ; after #define) Configures program to
    run on hardware with an LED on digital line 13, which
    makes sense as there's already one on that line, on the
    Diecimila itself. You can also have a "bigger, better"
    LED attached. Just be sure to connect it through a suitable
    resistor to the ground line.
 You can move the resistors, have them driven by other lines.
    If you do, JUST change these DEFINEs to reflect your
    hardware choices */
#define ledPin1 12
//#define ledPin2 11

#define Sw0 8 /* Configure the program to look at digital
  line 8 when asked to read "Sw0"... "SWitch zero"
  Wire it for closed pulls line low (i.e. to 0v)*/

void setup()
{
/*Start, second block of things for tkbOutput*/
  pinMode(ledPin0, OUTPUT);
  pinMode(ledPin1, OUTPUT);
//  pinMode(ledPin2, OUTPUT);

//Establish initial LED states... both off....
    digitalWrite(ledPin0,LOW); //turn LED off
    digitalWrite(ledPin1,LOW);
/*  digitalWrite(ledPin2,LOW); Not needed for program
      as written, but part of an easy extension*/

// Next set the state of boItWasOn according to the state
//   of things at the outset....
  if (digitalRead(Sw0)==1)
     {boItWasOn=true;} // Store "true" if switch pressed/ on
    else
     {boItWasOn=false;}; // Otherwise store "false"
  boItIsOn=boItWasOn;//Establish initial state for boItIs
}

void loop()
{

 //First block... wait for 1st change....
do {
  if (digitalRead(Sw0)==1)
     {boItIsOn=true;} // Store "true" if switch pressed/ on
    else
     {boItIsOn=false;}; // Otherwise store "false"
}
while (not(boItWasOn xor boItIsOn));
//On change, update boItWasOn...
boItWasOn=boItIsOn;
//.. and alter outputs....
digitalWrite(ledPin0,HIGH);
digitalWrite(ledPin1,LOW);//"Not" needed... sometimes!
delay(300);//debounce

//... first block ends here.
}

We all like the quiet life. It will, however, probably pay to type these things in by hand, except when they are very long. That way you REALLY look at every detail, you build certain skills, and you move closer to the day when you can enter code of your own devising.

Do the above by hand, if only for the following exercise.

Just after where it says....

 //First block... wait for 1st change....

.... there's a "do" loop consisting of....

do {
  if (digitalRead(Sw0)==1)
     {boItIsOn=true;} // Store "true" if switch pressed/ on
    else
     {boItIsOn=false;}; // Otherwise store "false"
}

Start the entry of that block by typing just the basic skeleton of a "do" loop....

do
{

}
while ();

Then go back and "fill in" the rest. If you do this sort of thing this way, it will save you a lot of hassle over the various sorts of brackets... { } and ( )... and the semicolons.

"If" statements are another that benefit from being entered in skeleton form first. What ends up as....

  if (digitalRead(Sw0)==1)
     {boItWasOn=true;} // Store "true" if switch pressed/ on
    else
     {boItWasOn=false;}; // Otherwise store "false"

... might usefully have been started from....

if ()
     {}
    else
     {};

...and then built up. Remember that the things you put inside the { } brackets should have a semicolon after them. That's a bit like the fact that even the sentence at the end of a paragraph needs its period. (i.e. "full stop".)



Anyway... Onward! Next, a simple matter: The variable names. You could call them Dick and Jane. But it is better to use names that mean something. The "bo" prefix doesn't MAKE the variables Boolean... the declaration (Boolean boItWasOn) does that... but if you use such variable name prefixes, it will help you remember what the variables are being used for. I know... this isn't a common practice in C programming... but it is in some other languages! Maybe the C people should "try it", see if they "like it"! I normally prefix variables of type byte with a b.

The Boolean type is named after George Boole, 1815-1864. Wikipedia has a fascinating article on him.)

Look at the "if..." statement....
  if (digitalRead(Sw0)==1)
     {boItWasOn=true;}
    else
     {boItWasOn=false;};

Wouldn't it be nice if we could just say....

boItWasOn=digitalRead(Sw0)

That (almost) would work in some languages. (We could even do something similar in Arduino's language, but if we did you wouldn't get the benefit of what doing it our way illustrates!)

All we want is to store in boItWasOn something to tell us what the state of the switch was at the moment this line was executed.

If we had a byte type variable called, say, "bItWasOn" (N.B.: bIt..., not boIt...), we could say...

boItWasOn=digitalRead(Sw0)

... but if we did, we'd have to remember a "code": a "1" in bItWasOn would mean "yes, it WAS on", and "0" would mean "no, it was NOT on." Not an impossible mental burden. But with Boolean variables, what's stored is "true" or "false". Use Booleans (where appropriate) and good variable names, and save your gray cells for the things that can't be taken care of any other way.

A programming text might (quite properly) say "boItWasOn returns a Boolean value". Is the meaning of this clear to novices in my readership? What it is saying is that when the program comes across boItWasOn, in the place of boItWasOn one is left with something drawn from the list of possible values for a Boolean type datum, i.e. "true" or "false".

The only values which can be held in a byte type variable are 0, 1, 2, 3.... 254, 255.

Moving on....

Look at the condition that controls our exit from the "do... while..." loop:

not(boItWasOn xor boItIsOn)

That sort of thing is best read from the inside to the outside. We have two things that "boil down" to either "true" or "false". They may both be true, both false, or a mixture.

Whatever they are, when you take two Boolean "things" and stick "xor" between them, you create a bigger thing that itself "boils down" to a simple "true" or "false".

Which means that what we had before boils down to....

not( something Boolean )

What "not" does is really simple: If the "something Boolean" is true, then "not ( that thing )" is false. ("not true"... get it?). Alternatively, if the "something Boolean" is false, then "not ( that thing )" is true.

Simple principles. Hard to put into words!

The ideas of "boiling things down" and of something "returning" something are important. It lets you look at a program at different levels. Sometimes you need to concentrate on detail; sometimes on the broader picture.

What does it do??? Why is it in the program???

the....

while (not(boItWasOn xor boItIsOn))

... will send the program back to go through the loop again until such time as the values in boItWasOn and boItIsOn are different. We could, in this case, have used != which counts as "are not equal".... but I wanted to show you the special Boolean alternative. There are times when only it will do.

"xor" is a Boolean operator. What is an operator? Think of it as a way of binding things together. In ordinary arithmetic, we can say 3+4. The plus sign is an operator. If we perform the "add" operation on three and four, the result is seven. In effect, we have "bound" the three and the four together into the more compact seven. The three and the four have been "boiled down" into a seven by the adding operation. The plus sign was the operator telling us what operation was required.

The Boolean operator "xor" was named from the words "exclusive or". It is a special case of the "or" operator. When you use the ordinary "or" to combine two boolean things, the result, the boiled down, bound up, single Boolean answer is "true" if the first OR the second of the things you were ORing together were true. This includes the case where both of the inputs are true. The EXCLUSIVE "or" excludes this case. boItIs xor boItWas only boils down to something true if boItIs is true, OR if boItWas is true, but the result is false (not true) if both of them are true. If I win the lottery OR my best friend arrives for a visit, it boils down to "I am a happy camper."

While we don't need them for this program, we might as well get the other two Boolean operators out of the way.

The Boolean operator "and" works as follows: If you "and" (yes, it is used as a verb here) two Boolean things together, the result, the answer is "true" only if the first AND the second of the things you were ANDing together were true. If I have a gun AND I have some bullets, then I can go shooting.

And the last Boolean operator you have already met: "not". Not(boSomething) will be true when boSomething is false, and not(boSomething) will be false when boSomething is true.

=====

Whew! It does get easier! Remember learning to ride a bike? And I'll tell you another thing: Over the years, I've seen something again and again. If you begin to get that "I'll never manage this" feeling, especially if it is growing, you may well be just on the cusp of the breakthrough, after which you wonder "Well why couldn't I do that yesterday?" Be it riding a bike or playing a piano or programming... many people CAN do it... why not you? And those who are able to do it cannot really tell you how. They just "know" how.

Pressing on....

An aside:

In programming the Arduino, there is nothing wrong with a little loop like the one we've made.....

do {
  //stuff
}
while (/*condition*/);

..... as long as you don't mind the loop going on until the power fails or the reset button is pressed, in some cases, or until "stuff" does something to make the "condition" false.

However: Be warned of two things.....

1)) Such loops can lead to programs that "don't work." If there is a fault in your logic, a bug, then the program will do what you told it to do... even if that's not what you wanted. If you have a program that "isn't working", look closely. You may find a place where you thought the program was going to go on, leave a loop, move to the next thing... but where it isn't doing so.

2)) You would not want to set up such a loop if writing a program to run in a computer controlled by Windows. Not important to most readers, I suspect, but I just mention it "in case". (You can certainly write such loops using a Windows computer, as long as the program is to run in something else, e.g. the Arduino.)

Right. We now have the Arduino counting from zero (all LEDs off) to one (1st LED on, other off.)

===== START OF OPTIONAL BIT ========

(If you find I worry too much about too many details, go ahead and skip ahead to the end of this optional bit.

When you work on programs, you should find that you complete "stages" from time to time. It is a bit like climbing Everest. You start in Katmandu, get things sorted out there, make sure all the bags arrived, that all the supplies you pre-ordered are at the warehouse. You then take a deep breath (well, as deep as it can be at nearly 1400m / 4500 ft. (Did you know that Google takes "What is 1370m in feet?" in it's stride?)) You draw a line. When you get your first camp on the mountain set up, you draw a line. Etc. It should be the same in programming. We're at a "draw a line" point. So....

Our current program has.....

plt1d_1

... as its first line, so I hope you either changed that line, or saved it as "plt1d_1". (If you set up a folder for the Sheepdog Tutorials, then you can use my names without any danger of clashing with things you wanted to name plt1d_1. Divide and conquer.)

Do a "SaveAs" now, saving the program as plt1d_2.

Change the first, second and third lines of the program, and save again. We are now setting out from one of our camps. If it all goes horribly wrong, we can close the mess we've made, delete the failed "assault on camp n+1", and re-open, in this case, pltd_1, and know that we are back to an unsullied "worked as far as it went" early stage.

All of the above applies to any work you do on a computer. If you have something that mostly works, and you want to move on, and tinker with things that may upset apple carts, use the technique explained above. Or at least use your computer's file handling tools to make a copy of the "working" version under a name like "pltd_1BUat12Feb08", and be sure to change the version ID in the version you are taking forward. This approach is fine when you will have no further use for the early versions of the program. It is a system I use more frequently than the one used during this tutorial. The tutorial generates "pltd_1", pltd_2" etc because I may want eventually to provide the different versions in a .zip file for readers.

====== END OF OPTIONAL BIT =========

Just after.....

//... first block ends here.

.... insert....

//Second block... wait for next change....
do {
  if (digitalRead(Sw0)==1)
     {boItIsOn=true;} // Store "true" if switch pressed/ on
    else
     {boItIsOn=false;}; // Otherwise store "false"
}
while (not(boItWasOn xor boItIsOn));
//On change, update boItWasOn...
boItWasOn=boItIsOn;
//.. and alter outputs....
digitalWrite(ledPin0,LOW);
digitalWrite(ledPin1,HIGH);
delay(300);//debounce

//... second block ends here.

If I were you, I'd just copy and paste the above. However, if you were writing this from scratch, you would find that you could copy and paste the first block, and make just one or two tiny changes, and have the second block up and running in no time. More on this in a moment. First, lets get that addition running.

With the addition, your Arduino should be able to count to two!!! When it comes on, both LEDs are off ("zero"), when the state of the switch changes, the first LED comes on ("1"). Remember that if you have a push button, not a toggle, switch, you need to keep it down until you are ready to go on and have the Arduino turn the first LED off, and the other one on. This represents "2".

Change the state of the switch again, and I think the display will go back to "1", then "2" on the next change, "1", "2", "1"... etc... At this stage the program probably only shows "zero" once. Don't worry about that just now.

Once that's working, just after block 2, add.....

//3rd block... wait for next change....
do {
  if (digitalRead(Sw0)==1)
     {boItIsOn=true;} // Store "true" if switch pressed/ on
    else
     {boItIsOn=false;}; // Otherwise store "false"
}
while (not(boItWasOn xor boItIsOn));
//On change, update boItWasOn...
boItWasOn=boItIsOn;
//.. and alter outputs....
digitalWrite(ledPin0,HIGH);
digitalWrite(ledPin1,HIGH);
delay(300);//debounce

//... 3rd block ends here.

Again... that is almost identical to the first two blocks. This time we turn both LEDs on, which is binary for "3".

See how a program can look quite long, when in fact, in essence, it isn't very complex? All we're doing in each block "boils down" (there's that important concept again) to: "Wait for a change in the state of the switch, Change what the LEDs are showing, And then pause for a moment". Get it?

If you just said "Yeah- easy" had you thought before answering? Do you really understand? If you do, explain to me why the "pause for a moment is there.

For those of you not clear on the "pause for a moment bit", here's the story... how (easy) and why (not hard).

How: If we put "delay(500)" into a program then as soon as the program comes to that line it will cause the Arduino to "seize up" for 500 milliseconds, 500 thousandths of a second, half a second, and "do nothing". And after the half second, it will go on to whatever comes next.

(Aside: There's an advanced feature in the Arduino that allows you to interrupt not only such pauses but anything else. Sensibly enough, this feature is called the interrupt. But don't worry about it for a while! I only mention it to impress advanced microcontroller fans who are skimming this as a quick start into the wonderful world of Arduino work.)

Why: If you put a volt meter on the line from the switch to the Arduino, you would see either 5v or 0v, depending on the position of the switch. It is that voltage that the Arduino looks at when you say digitalRead(pin).

In human terms, when you change the state of the switch, the voltage changes from one (5v or 0v) to the other (0v or 5v). But in computer terms, where time is under a microscope, where things that last for tiny periods of time, something different happens. What to you is a change from 0 to 5 looks to the computer as..... 0 to 5 to 0 to 5 to 0 to 5 to 0 to 5. The whole chain of reversals will take only a tiny moment, and the voltage will end up the opposite of where it started, but that's what happens when you flip a toggle switch or press (or release) a pushbutton. The phenomenon is called "switch bounce", and has to do with the mechanical aspects of the contacts coming together or separating.

By putting the "delay(300)" in at the end of each block, we prevent the Arduino from seeing all of the extra voltage transitions. Once it sees the first change (or "edge" as it is usually known), it "sits" in the delay for three tenths of a second- long enough that all the on/ offs have passed by before it exits the delay, but not so long as to even register with the human watching the program in action.

====

And, finally, on to the last step. Again, we're merely adding another block, very like the ones we've added before. The program reaches this block after it has done the "make the LEDs say 1", "... say 2", and "... say 3" blocks. This one turns both LEDs off, taking the display back to "0".

That's it! The program's done. Congratulation, you did it!

If you have a third LED available, you might want to try the (should be) fairly easy exercise of adding four more blocks like the ones we've done so far. With them you can make the Arduino count 0, 1, 2, 3, 4, 5, 6, 7... at which point, you would go back to 0 again. 0-7, eight numbers. That little oddity is connected with why computer people count from zero. Why, for instance, I called the LEDs led0, led1 and led2, instead of the "what- you- might- have- expected" led1, led2, led3.

The following diagram shows you 0 -7 in binary. Each column is for one of the LEDs, each row is one of the eight numbers, in sequence. Our program already does the first 4 rows. 0 stands for "LED off" and X stands for "LED on". Maybe it was writing this the day before Valentine's 2008 inspired me to put all those X's and 0's in!

              0 0 0
              0 0 1
              0 1 0
              0 1 1
              1 0 0
              1 0 1
              1 1 0
              1 1 1

==============

In conclusion....

Although that program doesn't really have anything "wrong" with it, it is not nearly as elegant or as clever as it could be. I done an alternate version, just for the fun of it. It makes use of concepts that you haven't encountered yet in this series of tutorials, but you might want to have a look just to see that things can be neater, more compact.

I hope you made it this far. Sorry it was a "heavy meal", but if you endured the pain, you should have had significant gain.


Oh dear... one more thing....

Confession: This tutorial, the one on Arduino programming, is a derivative of a derivative! As such, it will sometimes "leap about" a bit

Another type of data that you will meet in your programming is string type data. Unless you add an LCD display to your Arduino (which I recommend!) you won't use string types in your Arduino programming, but I hope you'll also program bigger machines.

If I declare a string type variable, for instance by saying...

string sAStringVariable

Then I can put "anything" in it. Well. Sort of. As long as "anything" is a string of characters, I can put them in sAStringVariable.

So, for instance, I could put the following into it....

I can even put "numbers" in the variable. It could, for instance, hold "3.1415"... BUT: It can't do any arithmetic with the numbers, not while they are still in the string type variable. As with all types, the string type has strengths (and hold anything) and weaknesses (can't be used in arithmetic).

Mastering the palette of data types is principally a matter of learning each type's strengths and weaknesses.

Our string variable could hold "ThisYYexample". It could also hold "This Example". Note that in the second case, as far as the computer is concerned, the variable is still only holding one thing. It doesn't "see" the space character as somehow special.


Whew! You made it!



Please also note that I have two other sites, and that the following search will not include them. They have their own search buttons.

My Sheepdog Guides site.
My Arunet site.

Go to the sites above, and use their search buttons if you want to search them.
To search this site....

Search this site or the web powered by FreeFind

Site search Web search
The search engine merely looks for the words you type, so....
*    Spell them properly.
*    Don't bother with "How do I get rich?" That will merely return pages with "how", "do", "I"....

You can also search this site without using forms.
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.


Want a site hosted, or email? You can also help me if you sign up via this link to 1&1's services. (I wouldn't recommend them unless I was happy after several years as one of their customers, but yes, they do pay me if you use this link! As do the Google advertisers, about whom I know nothing, of course.)



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


Why does this page cause a script to run? Because of the Google panels, and the code for the search button. Why do I mention the script? Be sure you know all you need to about spyware.
Editor's Main Homepage
How to email or write this page's editor, Tom Boyd

....... P a g e . . . E n d s .....