HOME - - - - - - - TUTORIALS INDEX - - - - - - - - - - - - Other material for programmers

Delphi: Making checkboxes and Boolean variables solve your problem

This "tutorial" will be less "Now type this..." than most.

I wrote a small program, and I thought elements of it might be of interest or use to you, even though I doubt you'll need the program itself!

The program provides a timer to control a fan in my house. One button turns the fan on for ten minutes, then switches it off. Another starts the fan going on for the first ten minutes of each half hour, then off, then on for the first ten minutes of the next half hour... and so on, BUT: only for selected hours of the day. (There are 24 checkboxes to specify which hours.) There's a button to turn the fan off.

That gives you the basic idea of the function of the program. To actually control the fan, you need some external electronics plugged into your parallel port. More on this later, but even if you don't want to bother with the electronics or tie up your port, the program has things to show you. You can run it with a "virtual" fan provided in the software.

You can obtain the finished program .exe and the sourcecode....

....by clicking on download source code and .exe. That will transfer a zip archive to your machine. The program was written for Delphi 1, but it will load into Delphi 2 (and, I would guess, higher Delphis) for study. (It can be made to control a real fan under other Windows, but needs modifications not covered here.)

If you compile and run the program, it won't operate any fan you have connected for two or three reasons, but it will "do" everything except turn on the fan, and you can fix it to do the fan, too. The reasons it won't work are...

Within the procedures "FanOn" and "FanOff" I have remmed out the essential lines, the ones beginning "if boEnPort then...". These procedures were created to isolate the underlying code controlling the hardware. Using procedures thus is an important skill. With changes only in these two small procedures I can transfer my fan control software to a different system, with a very different interface between the fan and the computer. I might, for instance, turn it on / off via the serial port. I remmed out the essential lines because the program, with those lines active, should not be run in a computer which has something other than the fan control electronics connected to the parallel port. (If you remove the squiggle brackets around the "port[888]:=" code, remove laWarnDisabled, the label saying "See "FanOn" and "FanOff" code".)

Even once the lines are no longer remmed out, you won't turn the fan on simply by clicking the "Fan On" button. Again, to protect the machine, and to provide a way to work on the program when a printer is on the parallel port, there's a checkbox labelled "Turn on output to port 888", which is not checked, initially. Look at the occurances of boEnPort ("Boolean, Enable Port") in the program to see how this mechanism operates.

Lastly, as written, the program must be compiled with Delphi 1 and run on a Windows 3.1 machine. For purposes of studying it, it will compile in higher delpihs, if you leave the two hardware control lines remmed out.

I won't go into the details of the hardware control here. See my Guide to Parallel Port interfacing if you're interested. There are notes there about modifying "port[888]:=" onto alternatives for other Windows.

Let's look at FormCreate....

boEnPort is a boolean variable which keeps track of whether we want to enable outputs to the parallel port.

The call of FanOff invokes the low level procedure that (if boEnPort is true) actually turns the fan off. It is hard to be sure that every pc will default to the same intial state, and in any case, some other program may have been running. This program, by the way, is NOT "well-Windows-mannered"... it relies on you to refrain from invoking other things that might affect the hardware controlling the fan. FanOff also contains the code to update the onscreen indication of the fan's theoretical state. (I say theoretical, because if boEnPort is false, after "FanOn", laFanState.caption will say "ON" regardless of the actual fan's state.) The manipulation of laFanState.caption is deliberately put at this low level so that there are few ways for it to be wrong! FanOff also changes the state of boFanOn, so that we have that to tell us if the fan is on or off (theoretically) any time we need to know.

(There's a corresponding "FanOn" procedure.)

bMinsTilOff is set to 255 (the maximum value that a byte type variable can hold) to tell the program that it shouldn't be acting on bMinsTilOff until further notice. 255 is a rogue value. More on bMinsTilOff soon.

Most of the program's funtioning derives from the timer, "timerMins". I have a tutorial on timers, but, in a nutshell: You set the timer's interval to a number of milliseconds. Here, for normal use, we set it to 60000. The timer counts them off, and after that many, it does whatever you have set up the TimerMinsTimer procedure to do. The "if timerMins.interval,.60000..." line is there because during debuggin, you may bery well want to set the timer's interval to a smaller value to compress time. (Note, however, because of things we have not yet discussed, not all of this program's operatins can be tricked into operating in compressed time.)

If you want to provide something like the compressed time option for debugging, always try to do it in a way that can be adjusted with a single line of code, and try to provide yourself with warning of when the debugging mode is in action. A label that is visible only when debug mode is on is one handy way to protect yourself.

Moving on....

Before we look at the timerMinsTimer procedure, a little more about bMinsTilOff.

So far in this program, it is only used to implement the operation of the "Turn Fan on for Ten Minutes" button. It's use could be extended in several ways, but that's an exercise for you! For now, look at the buFanOnClick procedure. (That's a bad name- sorry- it should be "FanOnForSpecifiedPeriod", or somesuch.)

In buFanOnClick we see bMinsTillOff set to 10, and FanOn (which turns the fan on, and adjusts labels on the screen accordingly). So far so good... but where the program turns it off again is a little unclear so far! Don't worry... All will be revealed... later.

Just before we move on, I should tell you that if the user has clicked "Fan On For Ten Minutes", that will over-ride the "Turn On / Off Repeatedly" fnctin fo rthe next ten minutes. After that time, I think, and it is easy to mis-program this sort of thing!, the system will revert to whatever you reqested with the "Turn On / Off..." checkbox. Which way do you WANT the program to run? Now have you set it up that way? (The joys of programming....)

In a related vein: If you click "Fan Off", the fan will go off immediately, AND the request for the On / Off cycling will be cancelled. Look at the code. Isn't it simple to achieve something like this.... IF you have a well designed structure of checkboxes and other boolean entities keeping track of your wishes and states?

If you haven't done a lot with turning things on and off, you might want to look closely at cbEnablePortClick and be sure you understand what is going on.

And now for the heart of the program...

The heart of the program is the procedure timerMinsTimer.

This procedure executes every time the timer "times out", i.e. when timerMinsTimer.interval milliseconds have passed. Set timerMinsTimer.interval equal to 60000, and the timerMinsTimer procedure will execute once a minute.

Forgive me! I used a very crude system to implement the program's ability to operate the "on / off" cylcling only in certain hours of the day. The function SeeIfThisHourIsEnabled returns true or false depending on value in wHrs. wHrs is filled with the "hours" part of the time of day just before SeeIfThisHourIsEnabled is called. The value comes from a call to Delphi's built in "now" function.

The first bit of code in the main block of the timerMinsTimer procedure handles situations where the fan is on because bMinsTilOff has been set to a non-0, non-255 value.

I used bMinsTilOff:=bMinsTilOff-1 instead of dec(bMinsTilOff) because I read in the Delphi help file that no range checking is done during dec. bMinsTilOff should never equal a value outside of the range valid for bytes... but if everything that "shouldn't" happen in my programs stopped happening I'd stay awake nights worrying.

Look at the code. I think you'll see how it works as long as you remember that timerMinsTimer is automatically called once a minute.

Look at the next bit of code. "Decodetime" and "Now" are built into Delphi.

Just after decodetime, I assemble a string with the current time (HH:MM) and display that on the screen. Seeing this change is a reassurance that Windows hasn't frozen up. It is also a quick, easy way for the user to see that the computer's clock agrees with the real world.

The next six lines (including the two "end"s in the count), the lines beginning "if(cbFanOnOff.checked) and (bMinsTilOff=255)...." work as follows.

First: The block is skipped if "Turn On / Off Repeatedly" is not checked.
The block is skipped if bMinsTilOff is not 255. This will be the case when "Fan On for 10" has been clicked less than ten minutes ago. We are still counting down that "Fan on" command.

If the above conditions are met, we then go off to SeeIfThisHourEnabled returns "true". The code for checking whether this is one of the hours we want the fan to go on and off "boils down" to a simple "true" or "false". By putting that code in it's own procedure, we can look at the details when we need them, and not be distracted when we don't.

The line "if (boTmp) and ((wMin div 10)mod 3 = 0) then..." may need a little explanation. boTmp is true if this is one of the hours "on / off" behaviour is wanted. wMin was filled with the current "minutes past the hour" back when we called decodetime. If you div a number by 10, you get the integer part of dividing the number by 10. Examples: 0 div 10=0, 3 div 10=0, 10 div 10=1, 13 div 10=1, 58 div 10=5. In essence, we have stripped the "tens" digit off of the minutes past the hour. Now we apply "mod 3" to that. We are only going to encounter 0-5, so I'll just tell you what x mod 3 is for that range:
0 mod 3=0
1 mod 3=1
2 mod 3=2
3 mod 3=0
4 mod 3=1
5 mod 3=2
From that little table, I think you can see that
(wMin div 10)mod 3
will equal zero when the minutes past the hour (wMin) is in the ranges 0-9 and 30-39 (inclusive).

It may seem a little clumsy to "turn the fan on" once a minute for ten minutes, even though it isn't turned off after the first minute, and to "turn it off" once a minute for twenty minutes, but (if nothing else happens) the "turn it on/ off again" doesn't actually do anything. It's about like pushing down on a light switch that is already down. Furthermore, if the "Fan On for 10" button is clicked, the "on / off" code isn't called while the ten minutes tick by... but whenever it ends, if the "Turn On / Off" checkbox is checked, the program resumes the "on / off" cycle. It is not so much a "turn off FOR twenty minutes after being on" as a "Be off (when cycling) DURING 10-29 and 40-59 minutes past the hour."

That just about does it...

The only procedure we haven't discussed is cbFanOnOffClick. This is executed when you check the checkbox for "Turn On / Off repeatedly". I was torn here. In some ways, I would have like the fan to come on immediately, staying on for ten minutes from when the checkbox was checked, and then off for twenty, on for ten, etc. However, I decided that it was better if the "on" period was always 0-9 and 30-39 minutes past the hour. That way, while you were in the house, if the fan was on when it "should" be off, or vice versa, you had a better chance of noticing something was wrong.

Another little detail. Remember the checkbox cbEnablePort? It is not checked initially, so you must check it if you want the program to control a real fan. The default "do everything except change outputs" state is safer, but what if you have a system set up, and you want it to self boot in your absence after, say, a power failure? One way around that sort of problem (other than having a specially compiled version of the program with cbEnablePort checked by default) is to add the following to the FormCreate procedure, just after boEnPort:=false;....
ADD THIS:      if paramstr(1)="PortOn" then boEnPort:=true;

Compile the program. Following instructions are for Win9x, but similar things can be done with other Windows. Create a shortcut to the .exe file. Right click on the shortcut. Add "PortOn" to the shortcut's "target" designation. Put the shortcut in your "Start Menu | Programs | Start Up" folder. Now when you boot your computer, the fan control program will start, with boPortOn set to true. You have used a Command Line Parameter (clp). The programming shown here is very crude. It won's, for instance, recognise PORTON. but you could fix that, couldn't you? You can read more in my tutorial on clps.

I hope you found useful things in that!

            powered by FreeFind
  Site search Web search
Site Map    What's New    Search This search merely looks for the words you enter. It won't answer "Where can I download InpOut32?"
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.

Also: This search only searches the material on one of my websites.

      Click here to visit another of my sites.

      Click here to visit my third site.




Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: Yes.. I do enjoy compiling these things for you. I hope they are helpful. However... this doesn't pay my bills!!! Sheepdog Software (tm) is supposed to help do that, so if you found this stuff useful, (and you run a Windows or MS-DOS 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.

Link to Tutorials main page
Here is how you can contact this page's author, Tom Boyd.


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


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

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