This has good information, and a search button at the bottom of the page
I am old fashioned. I disavow any responsibility for things you may elect to do. In particular, I disavow any responsibility for any consequences arising from connections you make between devices. You can't just plug "anything" into your computer or anything else!. In particular, make sure that you aren't assuming that an RS-232 interface can be connected directly to, say, an Arduino or BASICstamp, or PIC or other microprocessor. There are notes on some of those issues on my serial ports page, for those of you who need them.
Enough "lawyer feeding".... let's turn to the Fun Stuff....
If you have already read my page "dt4t" (the illustrations just a little way down the page will be familiar), you can jump ahead to material HERE that isn't duplicated THERE!
This is one in a series of tutorials which show you how to link two devices using serial comms. In the tutorials, the "devices" are a Windows PC running programs written with Delphi and an Arduino... neat little, inexpensive, fun, capable microprocessor. However: Either device could be replaced by another... many of the issues discussed here are general, but I find that principles are best understood when backed up by examples of their application.
Click here if you want to know more about the source and format of these pages. It may be easier to read this if you re-size the window, so that it does not use the full width of your screen.
The Delphi sourcecode is available for download. The Arduino sourcecode is presented here in full, and you can copy it and paste it into your Arduino development environment for compilation and upload to your Arduino.
These pages are ROUGH and ILL-EDITED at this time. Explanations are sketchy. But the code DOES work. If this note is still here 15 September 2010, write and complain!
For many years, for a number of different reasons, I've wanted to hook a microprocessor to a "big" computer, and have them talk to one another. These days (August 2010) I am using the wonderful Arduino for microprocessor projects, and a Windows XP computer for my "main" work. The material below could be used with other devices. In particular, if your objective is to connect some serial device to a "big" PC, you will find helpful material, I hope.
In the diagram below, if you assume that the "some serial device" is a PC, you see two PCs attached to the Arduino. The one on the left is there for when you are setting up the Arduino to do whatever you want it to do with the other PC. It would normally unplugged, taken away after the system had been built.
The set up above is pretty general. Even without restricting what it implies, you can have two scenarios.
In order to explain the scenarios, I need to say what I mean by "Master" and "Slave" in the rest of this essay. A "master" device is in charge. The "slave" device will "speak when spoken to". It may get on with various things when it isn't commanded by the master to be doing other things, but it will not "come up" on the serial channel, aka "serial link" between master and slave unless the master has told it to. The master may "speak" to the slave in an imperious manner, just issuing a command, and assuming the slave will execute it without any reply. If, for instance, there was an LED attached to the slave, the master might send a "turn LED on" command". Or, the master might "say" "Turn on the LED, and tell me that you've done it." In the latter case, the master would have to be programmed to look for the slave's reply.
You can build systems like the above in which the "Some Serial Device" is the master, and the Arduino is the slave, and you can build systems the other way around (Arduino as master, other serial device as slave.) In the work so far within this tutorial, an example of a Windows XP computer as master, with an Arduino as slave has been created. (The hard parts of doing the reverse are all solved, and part of the first solution.)
It is all well and good thinking in general terms, but eventually it is necessary to get down to cases. For the work to be discussed, this is the set-up I was using....
The Wulfden module is a neat, inexpensive solution to the problems arising from the fact that the PC uses RS-232 serial comms and the Arduino is a TLL device. I'll try to expand on that later. (See my serial port page, for a badly written "answer", in the meantime.("Hardware" section, near top.)) N.B.: The "Rx"/"Tx" markings on the device, and on the diagram above cause people no end of confusion. They stand for "transmit" and "receive"... but relative to which device??? There's a signal FROM the PC TO the Arduino that is marked "Tx" above.. and we are transmitting from the PC to the Arduino.... but the Arduino might well call that line "Rx", as it is the line the Arduino is receiving on. Not rocket science, but easy to get turned around. Follow the diagram: Connect the signal marked "Tx" on the PA4B to digital pin "3" of the Arduino, the signal marked "Rx" on the PA4B to digital pin "4" of the Arduino, enter the Arduino programs given below, and all will be well.
I suppose this is the place to say something about the connection from the PC running the Arduino Development Environment to the Arduino. It goes to digital pins 0 and 1. I'm not doing anything "clever" here... that's just the normal connection, over which you send programs ("sketches") to the Arduino from the PC where they are compiled.
Apologies for that little diversion. The problems this tutorial sets about solving are rife with "little details", all of which have to be Got Right.
If you work your way though what is on this page, and it isn't easy at the moment, as it is a work in progress, you will have a better understanding of various things which are going on. Alternatively, especially if you have some Arduino and general serial comms experience, and (in any case) until this page has had more editing, you might want to jump ahead to a tutorial where a fancy 2 computer "master" / "slave" system is explained. That link jumps you past material at the start of the page, material similar to what you've already read here.
Some day I will re-write all of this, and give you tutorials of my usual style... nice, progressive, help you understand what's happening bit by bit tutorials.
I don't have time to write it like that now. The work of developing the code is well along. The code needs rough edges "sanded", but it WORKS. So for now, in the rest of this page, I'll just "dump" in your laps, the stages I followed to arrive on the brink of creating the "final" (for now!) stage.
If you jump ahead to the tutorial where a fancy 2 computer "master" / "slave" system is explained, you will find the full source code there. The source code for the stages of development leading to the "fancy" answer are also available, and listed below. (DD81, DD82, and supporting Arduino programs.)
Many, many of the underlying concepts upon which this system is built... especially the part in the Windows PC... were discussed, explained, etc, earlier in the series of tutorials which led to this one. In particular the one about DD80 is worth a visit if you are puzzled about things.
There are a lot of rems in the Delphi code to help you understand what is going on. You don't, by the way, have to do this with Delphi or with a Windows computer as the master. If you know how to connect something else to a serial stream, and can send things to the Arduino over a "Tx to other device" line, and read bytes received over a "Rx from other device" line, you can use the Arduino code I've presented here with the software and serial device of your choosing.
The rest of this is "scraps"... well written code, but badly written explanation... of a stepwise progress to the "finished product" given above.
To come... work in progress!
I will try to explain more fully what DD81 and 82 are.... later.
You can download the free sourcecode for DD81. The zip file also includes a compiled copy of the application. The compiled version is hard coded to operate on COM1 at 9600, with no handshake.
You can download the free sourcecode for DD82. The zip file also includes a compiled copy of the application. The compiled version is hard coded to operate on COM1 at 9600, with no handshake.
At the moment (03 Aug 2010, 13:17UK) the other 4 Arduino programs related to DD81-83, and leading up to "SerialArdAsSleave2" are presented in the next block. Sorry! Will sort out when and as I can. By the way, my program and file naming works like this....
I try to avoid series like "Whatsit", "Whatsit2", "Whatsit3"... But when I fall to human frailty, I have no "Whatsit1" in the series. The "Whatsit(no number)" is "Whatsit1", and "Whatsit2" is the second in the series.
Have you looked at my Windows freeware and shareware, mentioned to friends with kids, schools, and also non-kid learners, and even non-learners? You do that for me (for free) while I write this page for you, for free?!
Sorry... the best I can do for now is to "dump" the code for several Arduino programs here. They interface with DD18, DD82 or DD83. I will one day come back to this page and sort things out, but for now you at least have the working code to draw on. In particular, look at the inline rems.
//SerialWOther- Serial comms With anOther device /*This is a first step along a road to having an external serial device talking with an Arduino. Get this much working first... it deals with many issue, and approaches the problems in an order which MAY protect your big PCs serial port from SOME of the errors you might otherwise make. I worked with one PC running the Arduino Development Environment, providing me with the means of programming the Arduino, and of watching for the output of lines like Serial.print"a", which passed to the Arduino through the "programming" cable, the one on the Arduino lines 0 and 1, input to the Arduino, output from it, respectively. I ALSO had a second PC running. This one was running the freeware Windows terminal program "PuTTY", set up for 9600 baud, no handshake. On that, I could see the output from mySerialPort.print("A"), etc. I had the second PC connected to the Arduino via the second PC's serial port, through a Wulfden PA4B TTL/RS-232 adapter, and what is labeled "Rx" on the PA4B was connected to D3 of the Arduino, which was being used for OUTPUT. (The "Rx" on the PA4B is relative to the big PC (or other serial device "upstream" from the PA4B. The Arduino TRANSMITS (Tx) while the other device RECEIVES "Rx" In the next stage of this testing and development, the "Tx" from the PA4B will go to D2 in the Arduino. The Arduino will RECEIVE data via D2, from the PA4B's "Tx" line, fed from the other serial device. */ #include <NewSoftSerial.h> #define LEDpin 13//no; LED AND current limiting resistor to 5v //#define bArCa 12 //no; #define SwMom 12//no; A momentary switch to ground on this #define SerInToArdu 2//no ; here expand rem #define SerOutFrmArdu 3//no ; expand rem NewSoftSerial mySerialPort(SerInToArdu,SerOutFrmArdu);//expand rem void setup(){ pinMode(LEDpin,OUTPUT); pinMode(SwMom,INPUT); digitalWrite(SwMom,HIGH);//"Switch on" internal pull up pinMode(SerInToArdu,INPUT);//Not actually needed... put in //to reassure users as to data direction over //serial lines pinMode(SerOutFrmArdu,OUTPUT); mySerialPort.begin(9600); Serial.begin(9600); }; void loop(){ if (digitalRead(SwMom)==HIGH){ digitalWrite(LEDpin,LOW);//Make LOW to turn ON Serial.print("a"); mySerialPort.print("A");} else{ digitalWrite(LEDpin,HIGH); Serial.print("b"); mySerialPort.print("B");}; delay(100); };
SerialWOther2 //SerialWOther2- Serial comms With anOther device /*This is the second step along a road to having an external serial device talking with an Arduino. Get the material in SerialWOther (no "2") working first, then proceed with this program. I worked with one PC running the Arduino Development Environment, providing me with the means of programming the Arduino, and of watching for the output of lines like Serial.print"a", which passed to the Arduino through the "programming" cable, the one on the Arduino lines 0 and 1, input to the Arduino, output from it, respectively. I ALSO had a second PC running. This one was running the freeware Windows terminal program "PuTTY", set up for 9600 baud, no handshake. On that, I could see the output from mySerialPort.print("A"), etc. I had the second PC connected to the Arduino via the second PC's serial port, through a Wulfden PA4B TTL/RS-232 adapter, and what is labeled "Rx" on the PA4B was connected to D3 of the Arduino, which was being used for OUTPUT. (The "Rx" on the PA4B is relative to the big PC (or other serial device "upstream" from the PA4B. The Arduino TRANSMITS (Tx) while the other device RECEIVES "Rx" Since the first step in developing what we have, the "Tx" from the PA4B has been connected to D2 in the Arduino. The Arduino RECEIVEs data via D2, from the PA4B's "Tx" line, fed from the other serial device. At least with PuTTY, this program "works". It is CRUDE, and you have to be sharp eyed to SEE it working, but it works. As before, a's and b's and A's and B's go from the Arduino to it's serial monitor and the external serial device, as before (a or b depending on whether SwMom is pressed). What is new in this second stage of the development is that if you press a key on the external serial device, in this case, the "big" PC running PuTTY, you will see that character appear in the stream of a's and b's appearing in the Arduino's serial monitor. (First "flew": 1 August, 2010) At least SOME buffering is happening somewhere. Remember: I believe I set the big PCs port up with no handshaking... in any case, I don't know of any wires in my circuit to implement it. Even so... if I increase the delay (below) to 1000, and press XYZ very quickly on the big PC's keyboard, over the next three seconds, the serial monitor displays aXaYaZ. Hurrah! I think that NewSoftSerial buffered those data as they arrived, holding them for when they were asked for. You could make the bit inside... if (mySerialPort.available()>0){} ... more clever, with a loop to process all that is incoming before returning to the rest of "loop()", if you wanted to. I didn't! */ #include <NewSoftSerial.h> #define LEDpin 13//no; LED AND current limiting resistor to 5v //#define bArCa 12 //no; #define SwMom 12//no; A momentary switch to ground on this #define SerInToArdu 2//no ; here expand rem #define SerOutFrmArdu 3//no ; expand rem NewSoftSerial mySerialPort(SerInToArdu,SerOutFrmArdu);//expand rem void setup(){ pinMode(LEDpin,OUTPUT); pinMode(SwMom,INPUT); digitalWrite(SwMom,HIGH);//"Switch on" internal pull up pinMode(SerInToArdu,INPUT);//Not actually needed... put in //to reassure users as to data direction over //serial lines pinMode(SerOutFrmArdu,OUTPUT); mySerialPort.begin(9600); Serial.begin(9600); }; void loop(){ if (digitalRead(SwMom)==HIGH){ digitalWrite(LEDpin,LOW);//Make LOW to turn ON Serial.print("a"); mySerialPort.print("A");} else{ digitalWrite(LEDpin,HIGH); Serial.print("b"); mySerialPort.print("B");}; delay(80); if (mySerialPort.available()>0){ Serial.print(mySerialPort.read(),BYTE); }; };
//SerialWOther3- Serial comms With anOther device /*This is the third step along a road to having an external serial device talking with an Arduino. Get the material in SerialWOther2 working first, then proceed with this program. "3" is similar to "2", but sends 0,1,2,3,4,5,6,7,0,1,2,3... (the characters, not the numbers) I wanted to examine whether bytes were being dropped. I worked with one PC running the Arduino Development Environment, providing me with the means of programming the Arduino, and of watching for the output of lines like Serial.print"a", which passed to the Arduino through the "programming" cable, the one on the Arduino lines 0 and 1, input to the Arduino, output from it, respectively. I ALSO had a second PC running. This one was running the freeware Windows terminal program "PuTTY", set up for 9600 baud, no handshake. On that, I could see the output from mySerialPort.print("A"), etc. I had the second PC connected to the Arduino via the second PC's serial port, through a Wulfden PA4B TTL/RS-232 adapter, and what is labeled "Rx" on the PA4B was connected to D3 of the Arduino, which was being used for OUTPUT. (The "Rx" on the PA4B is relative to the big PC (or other serial device "upstream" from the PA4B. The Arduino TRANSMITS (Tx) while the other device RECEIVES "Rx" Since the first step in developing what we have, the "Tx" from the PA4B has been connected to D2 in the Arduino. The Arduino RECEIVEs data via D2, from the PA4B's "Tx" line, fed from the other serial device. At least with PuTTY, this program "works". It is CRUDE, and you have to be sharp eyed to SEE it working, but it works. As before, a's and b's and A's and B's go from the Arduino to it's serial monitor and the external serial device, as before (a or b depending on whether SwMom is pressed). What is new in this second stage of the development is that if you press a key on the external serial device, in this case, the "big" PC running PuTTY, you will see that character appear in the stream of a's and b's appearing in the Arduino's serial monitor. (First "flew": 1 August, 2010) At least SOME buffering is happening somewhere. Remember: I believe I set the big PCs port up with no handshaking... in any case, I don't know of any wires in my circuit to implement it. Even so... if I increase the delay (below) to 1000, and press XYZ very quickly on the big PC's keyboard, over the next three seconds, the serial monitor displays aXaYaZ. Hurrah! I think that NewSoftSerial buffered those data as they arrived, holding them for when they were asked for. You could make the bit inside... if (mySerialPort.available()>0){} ... more clever, with a loop to process all that is incoming before returning to the rest of "loop()", if you wanted to. I didn't! */ #include <NewSoftSerial.h> #define LEDpin 13//no; LED AND current limiting resistor to 5v //#define bArCa 12 //no; #define SwMom 12//no; A momentary switch to ground on this #define SerInToArdu 2//no ; here expand rem #define SerOutFrmArdu 3//no ; expand rem NewSoftSerial mySerialPort(SerInToArdu,SerOutFrmArdu);//expand rem byte bToSend=0; void setup(){ pinMode(LEDpin,OUTPUT); pinMode(SwMom,INPUT); digitalWrite(SwMom,HIGH);//"Switch on" internal pull up pinMode(SerInToArdu,INPUT);//Not actually needed... put in //to reassure users as to data direction over //seal lines pinMode(SerOutFrmArdu,OUTPUT); mySerialPort.begin(9600); Serial.begin(9600); }; void loop(){ if (digitalRead(SwMom)==HIGH){ digitalWrite(LEDpin,LOW);//Make LOW to turn ON Serial.print(48+bToSend,BYTE); //mySerialPort.print(BYTE,49+bToSend); // Previous "worked"... at least didn't report error! Odd. // But always printed zeros. mySerialPort.print(48+bToSend,BYTE); bToSend=bToSend+1; if (bToSend>7){bToSend=0;};} else{ digitalWrite(LEDpin,HIGH); Serial.print("b"); mySerialPort.print("B");}; delay(80); if (mySerialPort.available()>0){ Serial.print(mySerialPort.read(),BYTE); }; };
//SerialArdAsSlave- Arduino as slave to host connected over serial line /*MOSTLY working, some rough edges. Worst(?) rough edge: If the program in the connected serial device is running, e.g. DD82 is running in a Windows PC, then some gibberish seems to be sent to the connected device if the Arduino is reprogrammed while that other program runs. And the reply or two from the Arduino to things it sends is garbled (or maybe receiving program is taking time to "sort itself out". If program in other device, e.g. program DD82, is restarted after Arduino is reprogrammed or re-started, then all goes well from the first character sent from the master device, i.e. the connected serial device. This is the fourth step along a road to having an external serial device talking with an Arduino. Get the material in SerialWOther2 working first, then proceed with this program. "4" Has Arduino waiting for command from Master, then responding. I worked with one PC running the Arduino Development Environment, providing me with the means of programming the Arduino, and of watching for the output of lines like Serial.print"a", which passed to the Arduino through the "programming" cable, the one on the Arduino lines 0 and 1, input to the Arduino, output from it, respectively. I ALSO had a second PC running. This one was running the freeware Windows terminal program "PuTTY", set up for 9600 baud, no handshake. On that, I could see the output from mySerialPort.print("A"), etc. I had the second PC connected to the Arduino via the second PC's serial port, through a Wulfden PA4B TTL/RS-232 adapter, and what is labeled "Rx" on the PA4B was connected to D3 of the Arduino, which was being used for OUTPUT. (The "Rx" on the PA4B is relative to the big PC (or other serial device "upstream" from the PA4B. The Arduino TRANSMITS (Tx) while the other device RECEIVES "Rx" Since the first step in developing what we have, the "Tx" from the PA4B has been connected to D2 in the Arduino. The Arduino RECEIVEs data via D2, from the PA4B's "Tx" line, fed from the other serial device. At least with PuTTY, this program "works". It is CRUDE, and you have to be sharp eyed to SEE it working, but it works. As before, a's and b's and A's and B's go from the Arduino to it's serial monitor and the external serial device, as before (a or b depending on whether SwMom is pressed). What is new in this second stage of the development is that if you press a key on the external serial device, in this case, the "big" PC running PuTTY, you will see that character appear in the stream of a's and b's appearing in the Arduino's serial monitor. (First "flew": 1 August, 2010) N.B.: The serial monitor shows BOTH what the Arduino is SENDING and what the Arduino is RECEIVING. Messy, but works! At least SOME buffering is happening somewhere. Remember: I believe I set the big PCs port up with no handshaking... in any case, I don't know of any wires in my circuit to implement it. Even so... if I increase the delay (below) to 1000, and press XYZ very quickly on the big PC's keyboard, over the next three seconds, the serial monitor displays aXaYaZ. Hurrah! I think that NewSoftSerial buffered those data as they arrived, holding them for when they were asked for. You could make the bit inside... if (mySerialPort.available()>0){} ... more clever, with a loop to process all that is incoming before returning to the rest of "loop()", if you wanted to. I didn't! */ #include <NewSoftSerial.h> #define LEDpin 13//no; LED AND current limiting resistor to 5v //#define bArCa 12 //no; #define SwMom 12//no; A momentary switch to ground on this #define SerInToArdu 2//no ; here expand rem #define SerOutFrmArdu 3//no ; expand rem NewSoftSerial mySerialPort(SerInToArdu,SerOutFrmArdu);//expand rem byte bToSend=0; byte bCmndRecd=255;//255 rogue for nothing received void setup(){ pinMode(LEDpin,OUTPUT); pinMode(SwMom,INPUT); digitalWrite(SwMom,HIGH);//"Switch on" internal pull up pinMode(SerInToArdu,INPUT);//Not actually needed... put in //to reassure users as to data direction over //serial lines pinMode(SerOutFrmArdu,OUTPUT); mySerialPort.begin(9600); Serial.begin(9600); };//end "setup()" void loop(){ /* if (digitalRead(SwMom)==HIGH){ digitalWrite(LEDpin,LOW);//Make LOW to turn ON Serial.print(48+bToSend,BYTE); //mySerialPort.print(BYTE,49+bToSend); // Previous "worked"... at least didn't report error! Odd. // But always printed zeros. mySerialPort.print(48+bToSend,BYTE); bToSend=bToSend+1; if (bToSend>7){bToSend=0;};} else{ digitalWrite(LEDpin,HIGH); Serial.print("b"); mySerialPort.print("B");}; */ delay(80); if (mySerialPort.available()>0){ bCmndRecd=mySerialPort.read(); delay(10); Serial.print(bCmndRecd,BYTE); delay(10); if (bCmndRecd==97)SendMsg1();//97: "a".. Says "sent A", even though send "a" if (bCmndRecd==65)SendMsg1();//98: "A" if (bCmndRecd==98)SendMsg2();//98: "b".. Says "sent B", even though sent "b" if (bCmndRecd==66)SendMsg2();//97: "B" ; bCmndRecd=255; }; };//end of "loop()" void SendMsg1(){//Messy... it should be easy to do one general "send" function char sMsg[]="I was sent an A..."; byte bLast=18;//chars to send, e.g. 3 for "XYZ" for (byte bCount=0;bCount<bLast;bCount++){ //0, not 1, for 1st char. delay(10);//May not be necessary mySerialPort.print(sMsg[bCount],BYTE);}; /* mySerialPort.println(); <<-- THIS: Root of much evil... because of way display done by DD82. Special handling of a "start new line" character could be done, but would be hard to follow, and isn't necessary/ important for our purposes. */ delay(10);//May not be necessary };//end SendMsg1 void SendMsg2(){ char sMsg[]="I was sent a B...."; byte bLast=18;//chars to send, e.g. 3 for "XYZ" for (byte bCount=0;bCount<bLast;bCount++){ //0, not 1, for 1st char. delay(10);//May not be necessary mySerialPort.print(sMsg[bCount],BYTE);}; /* mySerialPort.println(); <<-- THIS: Root of much evil... because of way display done by DD82. Special handling of a "start new line" character could be done, but would be hard to follow, and isn't necessary/ important for our purposes. */ delay(10);//May not be necessary };//end SendMsg2
This isn't the place to explain the general principles of serial ports to you. I have a separate page with more general serial port information. (At 2 Apr 10, that page has a number of topics which need further development, but it already has some useful things for you.... among them information on RS-232 to TTL level shifting, and how to use an inexpensive USB device to provide your PC with a virtual serial port which works fine with the software developed in this tutorial. If you look a little farther, I think you will find that there are "serial port proxies", which allow you to have "a serial port"... on a computer that you are only "connected" to across a LAN, or even across the internet! Serial ports are NOT "easy", but they have been earning their keep for many, many years. Master them, and you have access to all sorts of things.)
If you've never done anything with serial ports, it may pay you to go to another one I wrote. That covers setting up a serial connection from a PC to some external device, but a connection that merely sends data. Even more basic that the objectives of the tutorial you are currently reading... but all of the things that you learn to do there are incorporated in what we will do here. You can make your headache less painful by attacking the job in two halves. But even if you don't take my advice, the page you are reading should be all you need to get started with bi-directional serial comms.
Apologies to those of you who read the tutorial for the program before this one. (This one: DD81. The one before: DD80.) Much of the material below is just a re-cap of things already said in the discussion of DD80.
Start creating the application. I encourage the following general working practices: Choose a robust name for it, and create a folder, probably within something named something like "Delphi prj-tkb", if your initials are "tkb". To follow along with what is to come, you can put the application's folder anywhere you like, but call it DD81. (For Eighty-First Delphi Demo).
Fire up your Delphi... I'm using Delphi 7 under Windows XP, but what is here should work in earlier Delphis, too. Put a button on the form, don't bother to change even its name yet. Name the form they are on DD81f1. Give it a good caption. The caption determines what will be in the window's title bar when you run it. Save the unit as DD81u1.pas (in a DD81 folder), and save the project as DD80.dpr. (The system will create the essential DD81u1.dfm, and a bunch of other things that aren't essential to backing up your work.)
(I'll try to write the rest of this for you later, but it won't be soon. For now, just load the finished sourcecode from the download!
|
Click here if you're feeling kind! (Promotes my site via "Top100Borland")
If you visit 1&1's site from here, it helps me. They host my website, and I wouldn't put this link up for them if I wasn't happy with their service. They offer things for the beginner and the corporation.
Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org
....... P a g e . . . E n d s .....