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

PAGE TITLE

This is one of a collection of pages which, together, attempt 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, if you want that.

Please visit my page about power browsing notes sometime.

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

========

THIS IS STILL IN A "raw" STATE

Come back later if you don't like typos, etc.

The code WORKED... but in displaying it as HTML

distortions may have crept in

========

This is one page in a series which track the deveopment of an Arduino based Access Control device (electronic lock).

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

This the second of a long series of tutorials, but they are, I hope!, worth slogging through.

If you haven't read the first in the series, you really need to, at least to see the definition of the problem we are setting out to solve.

===

We defined our objectives... in detail... which is important to do BEFORE you start writing the software... in the first tutorial in this series. And made a small start towards our destination.

Now let's take care of having the "lock" open while the "Success" message is flashing, and, of course, locked at other times!

This matter of having the lock right is a very pale example of something you will have to think carefully about in lots of Arduino work: States: The STATE of the lock- "locked" or "unlocked" needs careful consideration. Only if you take care will the lock START in the right state, and switch back and forth between locked and not locked only at the right times. With other stateful things you'll encounter in other design problems, you will find things that exist in more than a mere two states. And sometimes the state of "A" and the state of "B" will be interconnected. If each can only exist in only two states, that will still leave you with four combinations of A's state + B's state.

Whew! Glad that's for another time. Back to the lock and its states.

In all probability, the lock will be locked if it isn't supplied with electricity, and unlocked when it is. Even so, partly because of the intricities of the driver electronics, which we are not going into, we are going to program our system so that is it trivial to adapt from one situation where the lock works as just described to a system where the reverse pertains. (Electricity LOCKs the door, no electricity UNlocks it.)

Yes, you guessed it... more functions.

One called LockDoor, the other called UnlockDoor. Such imaginative names!

And, for testing purposes, the green button ("green for go") will unlock the door, the yellow one will lock it. (Remembering all the while (in spite of all the OTHER things to remember!) that for now "lock the door" just means "turn LEDlock on", and unlock it means turn the LED off.

So here's a program that does the above. The functions for "SayFailed" and "SaySuccess..." remain in the code for our use later, but are not doing anything at the moment. Very little is new in the following... just the two functions at the bottom, and the tiny re-write of the still tiny "loop" function.

/*FEAa1AccCont2nd: access control system-  2nd stage of development
ver 27 Dec 09

Requires 3 LEDs, two switches.
One of the LEDs is standing in for an electromechanical strikeplate

Two of the LEDs present a "challenge" to the user
If the user holds down the right combination on
the switches for a short while, the "lock" opens for
a time.

The lock doesn't open WHEN the user presses the right
combination on the switches... that would make trial and
error too easy. Rather, the software looks at the switches
at a pre-determined moment, and if they are properly
set at that moment, the door opens... or at least the "lock" LED turns
on for a set period.

The code in FEAa1AccContStart doesn't do all of that... it just
makes a start along the road to it.

*/

const byte LEDlock=13;//Pin where lock would be connected.
const byte LED1=12;//LED1 connected here (MS bit)
const byte LED0=11;//LED0 connected here (LS bit)

const byte bGrn0=4;//pin green switch is on
const byte bYel1=5;//pin yellow switch is on
// N.B.: That is "ywe-ee-ell-one" for YELlow, bit ONE

void setup()//"setup" always present
{
   pinMode(LEDlock,OUTPUT);
   pinMode(LED0,OUTPUT);
   pinMode(LED1,OUTPUT);

   pinMode(bGrn0,INPUT);
   pinMode(bYel1,INPUT);

   setLEDs(0,0);
}

void loop()//This function always present
{
  if (boSwitchPressed(bYel1))
     {LockDoor();};

  if (boSwitchPressed(bGrn0))
     {UnlockDoor();};

}//end of function "loop"

boolean boSwitchPressed(byte bWhichSwitch)
//In this version, the program does NOT wait for the switch
//to be released.
  {
   boolean boAns=false;//What is in this will eventually be passed
     //back to the calling code. Assume switch NOT pressed,
     //for the moment.
   if (digitalRead(bWhichSwitch)==LOW)
     {
        delay(20);
        boAns=true;
     }//
     return boAns;
  }//end of boSwitchPressed

void SayFailed()
{
FlashLEDs(12,60);
}//end of SayFailed

void SaySuccessAndOpenDoorDuringMessage()
//That name looks silly... but it will appear only
//once, and the extra information will help you
//be clear about what is going on.

//The stuff to unlock the door WILL be part of this,
//.... later.
{
FlashLEDs(6,200);
}//end of SaySuccess...

void setLEDs(byte bL1, byte bL0)
{
   if (bL0==0) digitalWrite(LED0,LOW);
               else  digitalWrite(LED0,HIGH);
   if (bL1==0) digitalWrite(LED1,LOW);
               else  digitalWrite(LED1,HIGH);
 }

void FlashLEDs(byte bReps,byte bRate)
//Call this a number of times to cause both LEDs to
//flash on-off-on-off. The number in bRate controls
//how fast the LEDs flash.
{
  for (int i=0;i<bReps;i++)
   {
      setLEDs(1,1);
      delay(bRate);
      setLEDs(0,0);
      delay(bRate);
   }//end of "for..."
}

void LockDoor()
{
digitalWrite(LEDlock,HIGH);
}

void UnlockDoor()
{
digitalWrite(LEDlock,LOW);
}

At this time, the details within "LockDoor" and "UnlockDoor" are pretty simple. I don't care. It is STILL worth putting them in a function with a name that is VERY, VERY easy to grasp. Is it SO hard to remember that....

digitalWrite(LEDlock,LOW);

... unlocks the door? No. But, trust me, there are enough other things to think about while developing an application. Turn the above into "UnlockDoor", even if the details of HOW you unlock the door never get more complex. You'd be surprised how often they DO get more complex... and "tidied away" in dedicated functions, the complexities never trip you up when you are trying to deal with other things.

Onward!

Now that we have WAYS to lock and unlock the door, we need to consider carefully the "state" issues touched on earlier.

What state will the door be in before our system is even turned on. Power failures occur, you know! We'll work with the assumption that your electro-mechanical strikeplate is of the "unlocks when powered" variety, so all is well.

What state is the door in just after the program starts up? It is foolish to assume things, so just to be on the safe side, stick a call of the "LockDoor" function into the "setup" function of the program. It is there for that sort of thing!

Stick an "UnlockDoor" into "SaySuccessAndOpenDoorDuringMessage" right at the start of that function's code, and a "LockDoor" at the end. Can you get out of that function by some un-considered route? No. So you know that every time you unlock the door, it will relock shortly after.

You're done with a major part of the program! It may seem like it wasn't very hard. It isn't when the program has a good structure. You'd be surprised how easily you can create something with a bad structure which would not accept the "lock"/"unlock" provisions so simply.

Those improvements will appear in the next version of the code... but the next version will also have other advances.

We said that the access control system would present a "code" to the user in the form of the LEDs called LED1 and LED0 being on or off. Let's look at getting that to happen.

For that we're going to use the "random" function provided for the Arduino.

The ;language reference guide says....

"random(min, max)

"returns a random number between min and max-1 (long)"

(end quote).

I want to start with that perhaps mysterious "long" at the end of the entry.

That is saying that the number returned by the "random" function is of data type "long". A "long" type number is a whole number, potentially fromfrom -2,147,483,648 to 2,147,483,647.

Thank heavens for the "min" and "max" parameters"! I don't think I'm going to need the biggest possible numbers any time soon. With "min" and "max" I can tell "random" about the range that I do need numbers from. Note that the reference guide says BETWEEN "min" and "max". Thus, if I run the following, I should see various numbers, but always either 0,1,2 or 3. I shouldn't get a -1, I shouldn't get a 5, or even a 4 (max-1). THEY SAID that "random" would return numbers BETWEEN "min" and "max-1". Programmers have to be picky about such things.

void loop()
{
long longTmp;
longTmp=random(-1,5);
Serial.println(longTmp);
delay(200);
}

Ha! And I'm glad I did. They lied. The program above returns -1,0,1,2,3 and 4. Not in that order. It returns numbers from that list, in a "random" sequence.

Actually, the sequence isn't truely random... but it seems random. Except for one thing. Unless we take steps... to be explained in a moment... we could get the SAME sequence of numbers each time the program runs... depending how clever the Arduino people have been. (We can force that behavior, if they have been clever.)

So... before dealing with the "same sequence" issues, if I want "random" to pick one of the following: 0,1,2 and 3 for me each time it is called, I have to write....

random(0,4);

Be very careful with the supposed limits that a function will return. It is easy for mistakes... by you the reader, or by the guide authors, or by both of you! to creep in. There's nothing like a little test program to be certain it is doing what you thought it should.

So.... What about this "same sequence" issue?

Getting the SAME "random" numbers each time can actually be helpful during debugging work.

If, in the "setup" function you include....

randomSeed(5);

... you will get the same sequence of "random" numbers each time you run the program.

There's nothing special about the "5"... it was a number I picked out of a hat.

If you put....

randomSeed(6);

... into your program's "setup" function, you will also get the same sequence of "random" numbers each time you run the program... but this time, the sequence will be DIFFERENT from the one you got when you started off with randomSeed(5);

Now... suppose you put a different number into randomSeed each time you started the program? You'd get different random number sequences. But... without using "random", how can you get a random number?

I'm glad you asked!

There's another function we haven't met yet called "millis".

It can return a REALLY big number. It returns the number of milliseconds since the Arduino board began running the current program. (This number will overflow (go back to zero), after approximately 50 days.)

If we were to pass the number in millis to randomSeed after an arbitrary period of time, we would be passing an arbitray number to randomSeed.

I'm going to put....

randomSeed(millis);

into the "loop" function at a point AFTER the computer has waited for the human to press a button. We don't know quite when this will execute, so, happily for our purposes, we don't know quite what number will be passed to randomSeed.

There's a LITTLE flaw in my plan. Unless the people at Arduino HQ have done something to make random start randomly, the number which determines which LEDs are on for the FIRST challenge after the program starts will always be the same. But once a user has tried once to open the door, there is no way to know what the next number out of "random" will be, unless you can time your attempt to open the lock to a millisecond.

An aside: This "flaw" may seem trivial... but what if a burglar has a way to interrupt the power to the access control mechanism? He can then try again and again to find by trial and error the correct "answer" to just one of the many possible challenges. An unlikely scenario, but you have to consider all possiblilites if you want your designs to be good.

Continuing the aside: If the "flaw" really bothers you, solve it like this. (You'll have to write the boNoButtonPressed function first- trival):

Add to setup():

while(boNoButtonPressed) {SayFailed;};
randomSeed(BottomOfMillis());

(If you do this, you can take the randomSeed out of "loop()".)

Now when the control device starts up, the LEDs will flash rapidly forever, unless a button is pressed. Once a button is pressed, the device enters "normal" operation, with the random number generator "randomly" seeded.

I don't like this because it would have been a pain during debugging, and a user coming to the device just after a reset might just think it was broken, not think to try pressing a button.

(End of aside.)

We'll come back to millis another time... it is very useful; it can do things for you that "delay" can't.

So... we have a way of picking at random a 0, 1, 2 or 3. And we have two LEDs, which can show (in binary) 0,1,2 or 3!

Don't be alarmed if while you are testing this you sometimes get, say, a "3" several times in a row. The random number generator isn't "stuck" on 3, it is just a fact of life that if you roll a die several times it WILL... sometimes... give the same number several times in a row. In the next version of the program we will introduce some extra code to ensure that every time a use tries to open the lock, the user will see a challenge that is different from the immediately previous challenge.

Have a look at showInBinary(bNum) to see the crude way I implemented showing 0,1,2 or 3 on the LEDs.

To understand it, you need to know that....

((bNum & B10)==B10) will be true if the variable bNum holds (decimal) 2 or 3, which in binary are 10 and 11. (The "B10" in that says "the binary number "10")

And you need to know that ((bNum & 1)==1) will be true if the variable bNum holds (decimal) 1 or 3. (I haven't had to say "((bNum & B1)==B1)" because (decimal) 1 is the same as binary 1.)

Have a brief look at the showInBinary function. and then come back here for an explanation.

Oh! And our program now doesn't do anything but put a number on the LEDs and wait for you to press either button, at which point it puts a new number on the LEDs. It isn't even (yet) using the randomSeed thing I said so much about... because it isn't (yet) calling "SayFailed" or "SaySuccess..."

/*FEAa1AccCont3rd: access control system-  3rd stage of development
ver 27 Dec 09

Requires 3 LEDs, two switches.
One of the LEDs is standing in for an electromechanical strikeplate

Two of the LEDs present a "challenge" to the user
If the user holds down the right combination on
the switches for a short while, the "lock" opens for
a time.

The lock doesn't open WHEN the user presses the right
combination on the switches... that would make trial and
error too easy. Rather, the software looks at the switches
at a pre-determined moment, and if they are properly
set at that moment, the door opens... or at least the "lock" LED turns
on for a set period.

The code in FEAa1AccContStart doesn't do all of that... it just
makes a start along the road to it.

*/

const byte LEDlock=13;//Pin where lock would be connected.
const byte LED1=12;//LED1 connected here (MS bit)
const byte LED0=11;//LED0 connected here (LS bit)

const byte bGrn0=4;//pin green switch is on
const byte bYel1=5;//pin yellow switch is on
// N.B.: That is "ywe-ee-ell-one" for YELlow, bit ONE

void setup()//"setup" always present
{
   Serial.begin(9600);//for debug work only
   pinMode(LEDlock,OUTPUT);
   pinMode(LED0,OUTPUT);
   pinMode(LED1,OUTPUT);

   pinMode(bGrn0,INPUT);
   pinMode(bYel1,INPUT);

   setLEDs(0,0);
   LockDoor();
   }

void loop()//This function always present
{
  byte bNumToShow=random(0,4);//Pick 0,1,2 or 3 to appear on LEDs
  showInBinary(bNumToShow);//Show it.

  while ((!boSwitchPressed(bYel1))&&(!boSwitchPressed(bGrn0)))
     {;};//Wait for a switch to be pressed

  delay(100);

}//end of function "loop"

boolean boSwitchPressed(byte bWhichSwitch)
//In this version, the program does NOT wait for the switch
//to be released.
  {
   boolean boAns=false;//What is in this will eventually be passed
     //back to the calling code. Assume switch NOT pressed,
     //for the moment.
   if (digitalRead(bWhichSwitch)==LOW)
     {
        delay(20);
        boAns=true;
     }//
     return boAns;
  }//end of boSwitchPressed

void SayFailed()
//Also reseeds random number generator.
//The "long()" around "millis" converts the data type of
//   what millis gives you into the dtaa type "randomSeed"
//   needs
{
   randomSeed(long(millis));
   FlashLEDs(12,60);
}//end of SayFailed

void SaySuccessAndOpenDoorDuringMessage()
//That name looks silly... but it will appear only
//once, and the extra information will help you
//be clear about what is going on.

//Also reseeds the random number generator
//The "long()" around "millis" converts the data type of
//   what millis gives you into the dtaa type "randomSeed"
//   needs
{
  randomSeed(long(millis));
  UnlockDoor();
  FlashLEDs(6,200);
  LockDoor();
}//end of SaySuccess...

void setLEDs(byte bL1, byte bL0)
{
   if (bL0==0) digitalWrite(LED0,LOW);
               else  digitalWrite(LED0,HIGH);
   if (bL1==0) digitalWrite(LED1,LOW);
               else  digitalWrite(LED1,HIGH);
 }

void FlashLEDs(byte bReps,byte bRate)
//Call this a number of times to cause both LEDs to
//flash on-off-on-off. The number in bRate controls
//how fast the LEDs flash.
{
  for (int i=0;i<bReps;i++)
   {
      setLEDs(1,1);
      delay(bRate);
      setLEDs(0,0);
      delay(bRate);
   }//end of "for..."
}

void LockDoor()
{
digitalWrite(LEDlock,HIGH);
}

void UnlockDoor()
{
digitalWrite(LEDlock,LOW);
}

void showInBinary(byte bNum)
{
//Set LED1 on or off depending on
//left hand digit in binary representation
//of number in bNum...

//For debut... send what's in bNum to the serial monitor,
//    so we can check the display is right...
//(The int() stuff around bNum converts it from byte type
//   data, which println can't handle, to an equivalent
//   int type datum, which println IS oaky with.

Serial.println(int(bNum));

if ((bNum & B10)==B10)
  {digitalWrite(LED1,HIGH);}
  else
  {digitalWrite(LED1,LOW);};

//Set LED0 on or off depending on
//right hand digit in binary representation
//of number in bNum...
  if ((bNum & 1)==1)
  {digitalWrite(LED0,HIGH);}
  else
  {digitalWrite(LED0,LOW);};
}

A couple of things arise in the table.

First Thing That Arises: Instead of the....

randomSeed(millis);

... I said you would see, you have....

randomSeed(BottomOfMillis);

The reason for this is as follows:

The number in parentheses after "randomSeed", it's "parameter", is supposed to be of type "long", or of type "int". (This information can be gleaned from the entry for randomSeed in the language reference).

Unfortunately, millis returns a number in "UNSIGNED long", a different data type.

Now, we could convert the "unsigned long" to a "long" just by using "long(millis)". By putting "millis" as the parameter to the conversion function "long", we convert the "unsigned long" type datum into a "long" type datum... and randomSeed is happy... but I was getting strange behaviour... which could have come from various sources, one of them being the way the long() function converts an unsigned long datum.

We do a very similar thing with the byte-type datum in "bNum" when we try to pass it to Serial.println, which wants an "int" type datum, not a byte type datum. By saying....

Serial.println(int(bNum))

... we are using the int() conversion function to turn the byte type datum into an int type datum, which keeps Serial.println happy.

There are a number of other conversion functions. Some won't make sense to you until we've talked about the float, string and char data types, but the principle of what they do is the same.

The Second Thing That Arises:

while ((!boSwitchPressed(bYel1))&&(!boSwitchPressed(bGrn0)))
     {;};//Wait for a switch to be pressed

... looks pretty fearsome, but it isn't as bad as it seems. It says....

WHILE (
   (NOT yellow switch pressed)
   AND
   (NOT green switch pressed)...

   DO NOTHING.... but keep going around this inner loop.

("Not" is accomplished with the exclamation mark, (!), remember, and the boolean "AND" is accomplished with the "&&"

The Third Thing That Arises: And this one is NOT easy....

In "showInBinary", we have two similar conditions. A "condition" remember is something that is true or false. They are....

((bNum & B10)==B10)

... and ...

((bNum & 1)==1)

The SINGLE ampersand (&) is a "bitwise AND operator". It takes two numbers, "writes them out in binary" (i.e. as 1s and zeros), and then combines the numbers, one column at a time.

In the first comparison, the program takes the number in bNum, and does a "bitwise AND" with it and "B10", in other words the binary number "10", which is two, in our everyday decimal.

Still hanging in there? It gets better. Re-read the above until everything except "What is a "bitwise AND" is clear.

Okay.... On to "bitwise AND"... by example. All of the numbers below are written in binary. In each group of three, above the line are two binary numbers. Below the line is the "answer", what you get if you do a bitwise AND between the numbers above the line....

00
10
__
00



01
10
__
00



10
10
__
10



11
10
__
10

Those four examples cover ALL of the possible cases of bNum & B10. Perhaps you have worked out what is happening? Each column is done separately. The answer for that column is a 1 if both of the numbers being "ANDed together" have a 1 in that column, otherwise the answer for that column is a zero.

And... by happy "coincidence", the condition is true whenever the number in bNum has a 1 in the left hand position, false otherwise. That is why we have what we do in the two parts of the "if... else..." statement that the condition appears in.

Now take the second "if... else" It is almost like the first, but this time, we are "looking at" the binary digit in the right hand side of bNum, and setting the right hand LED accordingly.

We asked the random number function to fill bNumToShow with 0,1,2 or 3 because that is the whole list of the numbers which can be shown on just two LEDs.

And that's ALMOST enough about bitwise operators for now. They ARE useful!... but you can be forgiven if you hope you don't encounter them again soon. Besides the bitwise AND, caused by a single ampersand, (&), there's a bitwise OR, caused by a single vertical bar, (|).

Other Things Arising from third stage of program's development? None occur to me.

Onward!

In the next stage, we will be nearly finished. We will re-work the program so that if both LEDs are on, or both LEDs are off, the user presses the green switch to open the door, and if only one LED is on, the user presses the yellow switch to open the door. This is a rather pathetic rule which would leave you with a not very secure door... but that much is easy to program. After we have that working, there's just one more stage of development to complete: The conversion of the program to work as before, but with a fancier "When should the door be opened" rule.

That's enough for now, I think? When you are ready, go on to part three.

Enjoy... while, please, remembering what I said about the pages about the development of the access control device being only in rough draft so far.




   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 WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org