Operating
Serial Communications
under Visual dBASE 7.01
by Nicolas Martin

including a short history of the origins of Serial Communications.
Introduction

Many databases applications, like those developed under Visual dBASE, are designed for a multi-user environment, where several clients share a common database. This type of distributed architecture depends upon the services provided by a network to interconnect several machines so that they can share and exchange information

To cope with particular requirements, it is sometimes necessary to connect a specific equipment to a local client machine such as a modem, and to exchange data with only this equipment. Different solutions are available for such connections, the best known types are parallel and serial connections. In this article, we are going to consider serial communications, available on all PCs, and compliant with the RS232-C standard.

This type of interface has been around since the beginning of the computer era, and, in many technical domains, remains a good and efficient way to perform point to point connections between two computers or equipment. Even now, in the Internet and worldwide network age, many users still connect to the world wide web using a modem that is linked to their computer with a “serial” cable using an RS232 interface.

Accordingly, computer applications — including Visual dBASE’s — are sometimes required to operate this RS 232 serial link to exchange data with equipment or another computer hooked to the machine on which it is running.

This article, while introducing a new release of a Visual dBASE component named SerialComm 1.21 (attached to this document) presents the fundamentals of serial communications, their specific operational requirements on PC computers, their use from the Win32 operating system, and the integration of the SerialComm custom class component in a Visual dBASE application.

The article is divided into the following chapters:

| Introduction Hardware basics Full duplex versus Half duplex communications Flow control, handshake Asynchronous communications Transmission parameters and errors Architecture of a Communication application under Windows Serial Communication under Visual dBASE 7 using the SerialComm 1.21 package Using the SerialComm 1.21 component An application of the SerialComm 1.21 component: a mini TTY console Conclusion

As a general introductory topic, a short retrospective of the origins of serial communications presents some of the more interesting facts that drove the transmission techniques from their discovery to modern technologies.
Hardware basics

This chapter will give a simple overview of the hardware basics needed to understand the way a serial communication port works, from a software point of view. A full description of the hardware methods and details to connect and operate the serial port is given in a separate document, hardware description, but it is not necessary to read it to understand the present article. It is included for the interested reader, so that all the topics from hardware to software are covered.

DTE and DCE Devices

Two terms are commonly used in serial communications, DTE and DCE. DTE stands for Data Terminal Equipment, and DCE stands for Data Communications Equipment. Your computer is a DTE device, while most other devices are usually DCE devices. A DTE is connected to a DCE with a straight cable, while two like devices must be connected with a cross wire one. See hardware description for more information about serial cables.

You can simply replace the term “DTE device” with “your PC” and the term “DCE device” with “remote device” in the following discussion. The following table gives the signals involved in the serial communication port:

Abbreviation | Full Name | Function
TD | Transmit Data | DTE Serial Data Output (TD) Data is sent from a DTE device to a DCE device on this wire.
RD | Receive Data | DTE Serial Data Input (RD) A DTE device receives data on the RD (receive data) wire.
RTS | Request To Send | The DTE device puts this line in a mark condition to tell the remote device that it is ready and able to receive data. If the DTE device is not able to receive data (typically because its receive buffer is almost full), it will use this line as a signal to the DCE to stop sending data. When the DTE device is ready to receive more data (i.e., after data has been removed from its receive buffer), it will place this line back in its original state.
CTS | Clear To Send | The complement of the RTS wire is CTS. The DCE device uses this line to tell the DTE device that it is ready to receive the data. Likewise, if the DCE device is unable to receive data, it will change this line’s state.
DSR | Data Set Ready | DSR (Data Set Ready) is the companion to DTR in the same way that CTS is to RTS.
SG | Signal Ground |
CD | Carrier Detect | A modem uses Carrier Detect to signal that it has made a connection with another modem, or has detected a carrier tone.
DTR | Data Terminal Ready | Its intended function is very similar to the RTS line. Some serial devices use DTR and DSR as signals to simply confirm that a device is connected and is turned on. The DTR and DSR lines were originally designed to provide an alternate method of hardware handshaking.
RI | Ring Indicator | A modem toggles the state of this line when an incoming call rings your phone.

Note: The Carrier Detect (CD) and the Ring Indicator (RI) lines are available only in connections to a modem. Because most modems transmit status information to a PC when either a carrier signal is detected (i.e., when a connection is made to another modem) or when the line is ringing, these two lines are rarely used.
Full duplex versus half duplex communications

Achieving a half duplex or a full duplex communication process can depend upon the transmission media, or the way the application handles the incoming stream of data.

- In half duplex communication mode, characters are sent only one way at a time. For instance, CW radio communications, or coaxial ethernet (10B2), are half duplex media that can support only one direction; otherwise, a collision would occur and the desired messages would be lost. An application can also be designed as half duplex if it cannot process simultaneous transmit and receive data streams.

- In full duplex communications mode, data can be exchanged in both ways simultaneously. In RS232 communications, full duplex transmission is possible, as one wire is dedicated to transmission and another to reception.

Performing full duplex communication is generally better because it is more efficient in terms of effective data flow, and does not require any particular protocol to arbitrate the channel. However, it requires the capability of simultaneously sending data and receiving incoming data be present at both ends.
Flow control, handshake

Flow control is needed when the application that sends data transmits the data at a higher rate than the application that receives this data can process it. For instance, if our DTE to DCE speed is several times faster than our DCE to DCE speed, sooner or later data is going to get lost in the DCE as buffers overflow. Flow control has two basic varieties: hardware or software.

- Hardware flow control, also known as RTS/CTS flow control, uses two wires in the serial cable. When the DTE computer wishes to send data, it activates the Request to Send line. If the DCE has room for this data, then the modem will reply by activating the Clear to Send line, and the computer starts sending data. If the DCE does not have the room, then it will not assert Clear to Send.

- Software flow control, sometimes expressed as Xon/Xoff, uses two extra characters (Xon and Xoff) transmitted in your data lines. Xon is normally indicated by the ASCII 17 character; the ASCII 19 character is used for Xoff. The DCE will have only a small buffer, so when the DTE computer fills it up, the DCE sends a Xoff character to tell the DTE computer to stop sending data. Once the DCE has room for more data, it then sends a Xon character and the computer sends more data. This type of flow control has the advantage not requiring any more wires since the characters are sent via the TD/RD lines. However, on slow links, each character requires about 10 bits, which can slow communications down.

Even though these two types of flow control can achieve the same results, it is generally better to use hardware flow control because it is more efficient. On the other hand, using both flow types of control is generally useless.
Asynchronous communications

Let’s consider the synchronization problem: the receiver is going to track the state of its receive wire line, and will see a succession of state changes, “0”s and “1”s, from which it will have to recognize the characters.

There are two possible ways to decode the incoming bitstream:

- The first one consists of sending a given pattern, called a Synchronization Character, that the receiver will recognize and deduce the start of a character. The Synchronization Character is always sent when the transmitter is not sending user data, in order for the receiver to “Synchronize” on the transmitter. This communication scheme is called “Synchronous communications”.

- The second one, with which we are dealing in this article, is called “Asynchronous”, and means “no synchronization”. It therefore does not require sending and receiving idle characters. However, the beginning and end of each byte of data must be identified by start and stop bits. The start bit indicates that the data byte is about to begin, and the stop bit(s) signals when it ends. The requirement to send these additional bits cause asynchronous communication rates to be slightly slower than synchronous rates. However, it has the advantage that the processor does not have to deal with the additional idle characters.

> An asynchronous line that is idle is identified with a value of 1, also called a mark state. By using this value to indicate that no data is currently being sent, the devices are able to distinguish between an idle state and a disconnected line. When a character is about to be transmitted, a start bit is sent. A start bit has a value of 0, also called a space state. Thus, when the line switches from a value of 1 to a value of 0, the receiver is alerted that a data character is about to come down the line.

Transmission parameters and errors

The Start bit

After receiving the start bit, the receiver can synchronize an internal clock, which depends on the line speed. The clock keeps tabs on the timing and samples the line at evenly spaced instants to determine the value of the transmitted bits.

The Data bits

There may either be 5, 6, 7, or 8 data bits, depending on the physical line configuration parameters. Both the receiver and the transmitter must agree on the number of data bits, as well as the baud rate. Almost all devices transmit data using either 7 or 8 data bits.

Notice that when only 7 data bits are employed, you cannot send ASCII values greater than 127. Likewise, using 5 bits limits the highest possible value to 31.

Baud Versus Bits Per Second

The baud unit has its origins in Jean Maurice Emile Baudot, who was an officer in the French Telegraph Service. He is credited with devising the first uniform-length 5-bit code for characters of the alphabet in the late 19th century. What baud really refers to is modulation rate or the number of times per second that a line changes state. This is not always the same as bits per second (BPS). If you connect two serial devices together using direct cables then baud and BPS are in fact the same. Thus, if you are running at 19200 BPS, the line is also changing states 19200 times per second. But when considering modems, this isn’t the case.

Because modems transfer signals over a telephone line, the baud rate is actually limited to a maximum of 2400 baud. This is a physical restriction of the lines provided by the phone company. The increased data throughput achieved with 9600 or higher baud modems is accomplished by using sophisticated phase modulation and data compression techniques.

The speed defines explicitly the size of each individual bit. In theory, any speed is possible, but only the following ones are available on a PC:

Available speeds, in bits per second (or baud)
110 | 4800 | 56000
300 | 9600 | 57600
600 | 14400 | 115200
1200 | 19200 | 128000 (*)
2400 | 38400 | 256000 (*)

(*) The speeds of 128000 and 256000 baud are not available on all hardware, and depend upon the type of circuit used.

The Stop bit(s)

After the data has been transmitted, a stop bit is sent. A stop bit has a value of 1, a mark state, and it can be detected correctly even if the previous data bit also had a value of 1. It is recognized because of the stop bit’s duration. Stop bits can be 1, 1.5, or 2 bit periods in length.

There can be different combinations of number of data bits and number of stop bits, presented in the following table:

Number of Data bits | Number of stop bits
8 | 1 or 2
7 | 1 or 2
6 | 1 or 2
5 | 1 or 1.5

The data sent using this method is said to be “framed”, i.e., the data is framed between a Start and Stop Bit(s). Should the Stop Bit(s) be received as a Logic 0, then a framing error will occur. This is a common occurrence, when each side is communicating at a different speed.

The parity bit

Besides the synchronization provided by the use of start and stop bits, an additional bit called a parity bit may optionally be transmitted along with the data, just between the data bits and the stop bit(s). A parity bit affords a small amount of error checking, to help detect data corruption that might occur during transmission. You can choose either even parity, odd parity, mark parity, space parity or none at all. When even or odd parity is being used, the number of marks (logical 1 bits) in each data byte are counted, and a single bit is transmitted following the data bits to indicate whether the number of 1 bits just sent is even or odd.

For example, when even parity is chosen, the parity bit is transmitted with a value of 0 if the number of preceding marks is an even number. For the binary value of 0110 0011 the parity bit would be 0. If even parity were in effect and the binary number 1101 0110 were sent, then the parity bit would be 1. Odd parity is just the opposite, and the parity bit is 0 when the number of mark bits in the preceding word is an odd number. Parity error checking is very rudimentary. While it will tell you if there is a single bit error in the character, it doesn’t show which bit was received in error. Also, if an even number of bits are in error, then the parity bit would not reflect any error at all.

Mark parity means that the parity bit is always set to the mark signal condition and likewise space parity always sends the parity bit in the space signal condition. Since these two parity options serve no useful purpose whatsoever, they are almost never used. SerialComm proposes however the “Mark” parity as a possible choice.

In summary, the parity bit follows the following rules:

Parity type | Description
None | No parity bit added
Odd | if number of 1 in the received character is odd, then parity bit is 0.
Even | If number of 1 in the received character is even, then parity bit is 0
Mark | Logic 1 (Idle state)

Each time a character is received by the receiving hardware, if the parity check is enabled, the hardware will recompute the corresponding parity and compare it with the parity bit received. If they are identical, the hardware declares it correct. If they are different, the hardware declares a parity error.

The Break Signal

Last, we can introduce the notion of “Break” Signal. This is when the data line is held in a Logic 0 state for a time long enough to send an entire character. Therefore if you don’t put the line back into an idle state, then the receiving hardware will interpret this as a “break” signal.

A sample waveform

The diagram below shows the expected waveform of a character when using 8 Data bits, No Parity and 1 Stop Bit. The RS-232 line, when idle, is in the Mark State (Logic 1). A transmission starts with a start bit which is Logic 0. Then each bit is sent down the line, one at a time. The LSB (Least Significant Bit) is sent first. A Stop Bit (Logic 1) is then appended to the signal to make up the transmission.

The diagram shows the next bit after the Stop Bit to be Logic 0. This must mean another word is following, and this is its Start Bit. If there is no more data coming, then the receive line will stay in its idle state (Logic 1).

The above waveform applies to the Transmit and Receive lines on the RS-232 port that carry serial data. The other lines on the RS-232 port which, in essence are parallel lines (RTS, CTS, DSR, DTR, CD and RI) are also at RS-232 Logic Levels.
Architecture of a Communication application under Windows

Generally speaking, a communication application will be in charge of:

- sending the desired characters on the line
- receiving incoming characters
- managing the handshake signals and protocol.

This can be viewed also as sending and receiving, because managing input lines or incoming Xon/Xoff characters can be considered as a receiving process, while managing output lines or sending Xon/Xoff characters can be viewed as a sending operation.

Even if the two processes — sending and receiving — look rather symmetric and similar, from an algorithmic point of view, there are major differences bewteen them:

- The sending process is a synchronous process. That is to say, the application, here the communication program, knows exactly when it has to send characters or activate signals. When it decides to do so, it will call the corresponding function that will perform this task. If no timing issue is to be met, then the communication task simply waits until the function returns, and continues processing after.

- The receiving process is, on the other hand, asynchronous, because it simply does not know when the incoming data will be received, or when the control signals will change state. It will have to perform specific actions when such events occur. Functionally, there are two techniques, named polling and interrupt, to handle these events:

| Polling | Interrupt
Description | Consists of polling the communication port and status lines to check whether new data is available or if a control signal has changed. | Consists of calling the function that reads the communication device - and thus the new incoming character or signal state change - only when the communication device hardware signals it by generating a hardware interrupt. The function called when an interrupt occurs is named an “Interrupt Service Routine”, or ISR.
Advantages | Easy to implement. | Best method to deal with communication processes, because ISR is called only when needed.
Drawbacks | Very much CPU time consumed. High data flow rates cannot be achieved directly by this method. | More difficult to implement, debug, and maintain. Hardware closely dependent.

During the good old times of DOS applications, use of an interrupt-driven application was available only to assembly or C programmers, because they could directly access all the hardware resources of the machine. The drawbacks of such applications were that there was a high level of software complexity, they were difficult to debug, and they provided low maintenance abilities.

In a few words, an interrupt is a way to inform and signal a microprocessor which is executing a software program in memory, that external hardware events occur. An interrupt line corresponds to a hardware input, supervised by the microprocessor. When it becomes active, the microprocessor immediately stops its current execution to process another program (called the interrupt handler or the interrupt service routine) before returning to the code it was previously executing before the interruption arrived.

The arrival of Windows has swept away all these low level techniques, as this operating system controls the management of all the hardware resources of the machine. Applications must no longer directly address the hardware resources. An application addresses Windows, which, in turn, operates any hardware resource, especially the communication ports.

At first glance, this may look like a regression, because an interrupt handler cannot be anymore settled as before. But in fact, Windows kernel sets up already, to communicate with the serial ports, an interrupt process and fills up, when characters are received, an input buffer that the application can read when necessary. Let’s look in greater detail how the communication application is going to be designed now under Windows:

- The receiving data stream

The receiving stream can be seen as shown on the diagram provided further on. The main feature of this diagram consists in a receive (Rx) queue, that behaves as a FIFO buffer (First In, First Out):

- The receive queue is accessed in write mode by a Windows process, which is hardware interrupt-driven, at the frequency rate of the incoming characters. This frequency can be relatively high. For instance, at 9600 baud, the character rate can go up to 1000 Hz, or one character each 1 millisecond.

Note that the port status, both hardware control lines and Xon/Xoff protocol, is also entirely managed by Windows. The application merely needs to configure which handshake to use, and to check for protocol events.

- To get the incoming information from the COM port, two methods, similar to polling and interrupt, can be considered for this receive queue:

| Polling-like method | Interrupt-like method
Description | Ask Windows to get the next characters from the receive queue, extracting them from the FIFO buffer, and the port status. This is different from direct polling on the COM port, as discussed before, because here several characters can be read at a time. If characters are read 10 by 10, the read function is called at a tenth the maximum character frequency, and executes 1/10 times the same application code. | Windows is configured so that it signals the application the events that occur on the communication port. In Windows 16 bits, this was achieved using WM message. In Windows 32 bits, it uses thread synchronization and events. Both act like an interrupt process, with the difference being that the ISR will execute here only when the operating system activates.
Advantages | Easy to implement. Application code can be executed at a much lower frequency than the character frequency and can thus afford to be slower. | Most efficient method in terms of latency delay. Port status changes can be signaled.
Drawbacks | Requires a receiving queue long enough to store temporarily received characters between each read. Latency delay between the reception and the processing of characters; on the average, this delay is 1/2 the period at which the queue is read. Port status needs to be checked regularly. | Difficult to implement, because it requires multi-threading or message queue process by callback functions. Application’s receiving code executed at high frequency, requires optimized and fast application code. Stability within the O.S.

- The sending data stream

The sending stream is less complex, and is shown on the same following diagram. It also contains a transmit (Tx) queue, but here, this queue is not compulsory if we accept the transmit part of the communication application to be synchronous. That is to say, it will wait until the desired action on the communication port to be fulfilled before continuing further processing.

As for receiving, Windows can be configured so that the transmit process is asynchronous so that it can signal the application, with analog techniques, that the requested action has been fulfilled.

A simplified view of the communications scheme under Windows can be illustrated with the following diagram.

The Windows kernel sets up its Application Programming Interface (API) with which the application (in our case, the Visual dBASE program) dialogs. The Windows kernel also sets up the Receive (Rx) queue and the Transmit (Tx) queue, which can be accessed both by the Windows API functions, and by Windows Interrupt handlers. Those handlers work asynchronously with the application. They can dialog synchronously, through the hardware drivers, with the hardware, which in turn drives the physical line. The hardware can also signal events (that can be: new character received, control line state change, ...) to the interrupt handlers and to a control API, which in turn translates them into events to the application.
Serial Communication under Visual dBASE 7 using the SerialComm 1.21 package

Attached to this article is the SerialComm 1.21 software package that you can easily retrieve with the link at the bottom of this page. Here is the list of the Visual dBASE files it contains, along with a short description of each of them:

- SerialComm.cc

This file contains the component that operates the serial communication handler. Many properties and methods are provided to let you control the communication port configuration, including port parameters, error and status reporting, asynchronous reception and full duplex transmissions.

- StructureEx.cc

It contains a class named`structureEx`, which is an extension of the Structure.prg file that ships with Visual dBASE 7’s samples. It accesses the structure members faster, allows easy handling of C structures used with Win32 API calls with support of more C types, and enables direct bit field manipulation (which is especially convenient for serial communications structures). This class is 100 % directly compatible with the one defined in Structure.prg

- OneShotDemo.wfm

This demo form, intended to be used with a modem, simply sends a character string representing the AT Hayes command ATZ. This should make your modem reset.

- ContReadDemo.wfm

This demo form shows the mechanism proposed by SerialComm to easily handle the asynchronism of the reception process, so that the design of the communication application becomes really simple. To illustrate it, this sample sends a Hayes ATI command, so that the modem should reply its identification string.

- FullDuplexDemo.wfm

This demo will allow you to dialog directly with a host device, sending data and displaying received data from the line, in complete full duplex mode.

- TTYDemo.wfm

In order to show the new features added to version 1.2 of the SerialComm component since the previous version (1.01), this mini TTY terminal emulator will show how to process communication errors, port status, handshake methods, and how to deal with error situations.

Each file is self documented and contains detailed instructions of its use. This documentation is in each file’s header comment.

It is recommended, but not required, that you include the SerialComm component in your component palette, at least during the time you’re getting used to it and to the demo forms.

The four demo forms included in the package and listed above are of various levels of complexity. I recommend that you run and examine these demo programs in the following order:

1. OneShotDemo.wfm is the simplest one.
2. ContReadDemo.wfm shows simply the continuous read mechanism.
3. FullDuplexDemo.wfm, with which you can start exchanging data with a host.
4. TTYDemo.wfm, the most sophisticated, has many features that call most of the SerialComm component’s built-in functions. This form will be taken as a working example to illustrate the use of the SerialComm component later in this document.

But before getting into the demo form’s content, let’s look first in detail at how to use the SerialComm component.
Using the SerialComm 1.21 component

Now that the main substance concerning serial communications has been explored, let’s go back to our business applications and their need to communicate on serial communications.

Obviously, the choice between the polling-like and the interrupt-like methods will be determined by your needs:

- whether you need an application with fast real-time abilities and immediate reaction to external events and single character reception,
- you require reasonable performance, with the major concern being the ability to exchange data with satisfactory timings.

If you are in the first situation, then you should forget about Visual dBASE, go to something like C or C++ and use interrupt-like methods.

On the other hand, if your application deals mostly with data exchange through the RS-232C media, with rather weak real- time requirements, then the SerialComm component, using a polling-like method should be your choice.

This SerialComm component is contained in a custom class named SerialComm.cc. It contains the required properties and methods to operate a serial communications handler using the Windows API. It’s written totally in Visual dBASE 7 code, and provides an easy way to design your communication application under Visual dBASE.

The following description is a general guideline on how you should use this component and design your application, taking into account the general interface parameters. For details on a given parameter, please refer to the component’s documentation itself, contained in the source code’s comment header.

Let’s now go through the steps to design the communication application:

- Opening the communication port

In Windows 98 (32 bits), a communication port is seen exactly like a standard file. You first open it, write and read data, and finally close it. Opening a communication port requires, however, that you provide more parameters than you must to open a disk file. You need to provide the line’s physical parameters. These parameters are described in the Transmission parameters and errors section:

- The communication port name:`COM1`,`COM2`,`COM3` or `COM4` to which your serial cable is hooked
- the baud rate, from 300 to 115200 (or more if hardware supports it)
- the data bits per character, 5, 6, 7, or 8
- the number of stop bits, 1, 1.5 or 2
- the parity, if any, can be “None” (no parity), “Odd”, “Even”, or “Mark”

For instance, opening the`COM2` port at 9600 baud with no parity, 8 data bits, and 1 stop bit will be performed by:

| `oSerialCommRef.openPort( "COM2", 9600, "None", 8, 1 )`

where `oSerialCommRef` is the SerialComm component’s reference.

- Setting the protocol (handshake) type

The communication port is set by the `openport()` method to a default “no handshake” configuration. In this mode, the DTR and RTS signals will be activated, but these control lines, as well as the DSR and CTS signals, will not be used to control the incoming or outgoing data flow.

To set a particular protocol handshake method, two functions are provided in the component:`SetXonXoffConfig()` and `SetHandshake()`.

- If you intend to use the software Xon/Xoff protocol, then you first need to set the Xon/Xoff configuration using the `SetXonXoffConfig()` function. This will set:

- the Xon and Xoff characters,
- the limits (percentages) of the receive queue that will trigger the Xoff and Xon characters, and optionally, the size of the receive queue itself. The receive queue limits will be set as follows:

The Xoff limit, set as a percentage of the receive queue, defines a number of characters allowed in the Rx queue. If the number of characters in the receive queue exceeds this limit, an Xoff character is sent.

Likewise, the Xon limit, as a percentage of the receive queue, defines a lower limit. If an Xoff was previously sent, and the number of characters in the receive queue gets below the Xon limit, then an Xon character is sent.

When the port is opened, a default configuration for the Xon/Xoff characters and queue limits is set up. These default settings are the following:

- Xon ASCII code is set to 0x11 (= 17)
- Xoff ASCII code is set to 0x13 (= 19)
- Xon limit is set to 20%
- Xoff limit is set to 80%
- The Rx queue size is the default one, set up by Windows.

- Then, for both hardware and Xon/Xoff protocol, you will call the `SetHandshake()` method, passing the desired handshake type as a parameter. The available handshake types are the following:

- No handshake (set as default when the port is opened): 0
- Xon/Xoff: 1
- RTS/CTS and DSR/DTR: 2
- DTR/DSR only: 3
- RTS/CTS only: 4

Note that no possibility to mix the hardware protocol with the software protocol exists. As has been discussed earlier, it is useless to do so. However, if you require a specific protocol other than the ones available in this list, then it is possible to add your own. Use the corresponding section of the component’s source code, in the `SetHandshake()` function:

| `// If needed, one can add here additional handshaking methods; the dcb` `// changes can be set the same way as for the 5 methods proposed above.` `case ( HSmethod == 6 ) // My protocol` `// My protocol's statements` `otherwise ...`

- Reading data from the communication port

Now that we have successfully opened the COM port and set the protocol, we are ready to receive and transmit data on the serial line.

As we have seen previously, the thing to remember about the receiving end is that we don’t know when we’re going to receive characters on the line. There are two ways to get the incoming characters: by directly reading the communication port, or by using a polling-like method:

- Directly reading the communication port:
The simplest method is to issue repetitive read commands to the port, reading new characters if available. While always possible using the `readstring()` function of the component, this polling method is not recommended because it may occupy the CPU at full load while looping through the COM port.
- A polling-like method:
There is a more concise method to get the read process to behave more like the interrupt-driven scheme.

The idea here is to read several characters at a time from the input buffer in order to poll it less often. This allows less frequent execution of the part of code that processes those characters.

If we take into account the time Tc taken by a character to be received on the line, then we can compute the time taken by a group of characters to be received, assuming each character arrives immediately after the preceding one.

Suppose, in these conditions, we want to read a maximum of n characters at a time. Then we can ask for new characters at an interval equal to n times a character length. We will call this interval Tnc, which obviously is:

`(1) Tnc = n.Tc`

The character time Tc will depend upon the line parameters, defined when the port is opened. As we have seen before, a character will include, in addition to its data bits, one start bit, stop bit(s), and, if requested, a parity bit:

`(2) Tc (s) = (1 + databits + stopbits + iif( parity check, 1, 0)) / baudrate`

This is a first step. In order to be fully asynchronous and have our read task be called only if new characters have arrived, we need to set up an independent mechanism that will read the input buffer. This will be accomplished by a Visual dBASE timer, whose interval time will be set to Tnc. The timer is going to poll the communication port at this interval, and, if new characters are available, call a user function that will process those characters.

Here, we have to be careful with some timing and sizing issues:

1. Visual dBASE timers can run at a minimum interval time of 55 ms. Even if you set an interval time less than 55 ms, the timer will run at 55 ms. Thus, the parameter n should be chosen so that the Tnc interval time is greater than 55 ms, otherwise, and because reading will be performed during 55 ms and not during the expected lower interval time, more than n characters could be read during the Tnc interval.

`(3) Tnc > 55 ms`

2. The size of the receive buffer should be larger than n, as it must be able to contain n characters between two successive reads from the timer. In practice, because there can be some jitters on the timer ticks due to Windows or processing at that time, it is better to keep some margin and set a minimum size of 2.n. This is generally not a problem, as the default receive queue size set up by Windows will be generally of 4096 chars.

`(4) 2.n < rxBufSize`

3. Last, it is important that the processing time (Tp) the user routine will take to process each time the n maximum characters are received be less than the timer interval Tnc. Otherwise, the timer tick will be postponed until the user routine returns.

`(5) Tp < Tnc`

The second requirement necessary to cope with asynchronism is the capacity to call the function that processes the read characters only when needed, as would an interrupt routine.

As we don’t know when new characters arrive, the only way is to regularly read the input buffer for new characters, using a timer set to the Tnc interval. Because we read and process characters at a much lower rate (Tnc) than the character one (Tc), this will not be as time critical as if we were reading character by character. If new characters have arrived, then a function, whose reference (Visual dBASE Function Pointer) has been previously supplied, will be called.

This asynchronous process of regularly reading the receive queue is called here a “continuous read”.

In order to initiate a continuous read on the communication port, the `startContinuousRead(n,fpUserISR)` function will be used. This function will take 2 parameters as input:

- `n`: the maximum number of characters to read each time. In fact, the effective number of chars read each time can vary. It can, of course, be less than n if less characters have arrived, but it can sometimes be more than n if there are some jitters on the timer ticks, or if VdB is occupied when the tick arrives.

- `fpUserISR:` the Visual dBASE function pointer of the user ISR function that will be called each time new characters arrive on the port. When calling this function, a parameter will be passed, that will be a Visual dBASE string containing the new characters received since the last call.

For example, the user ISR can be declared like this in a form:

| `function processRxData( cNewChars )` `// process the characters in cNewChars` `return`

Let’s say we want to process, for instance, characters at a 100 ms rate (and larger than 55 ms), assuming that the port is opened at 9600 baud, 8 bits of data, no parity, and 1 stop bit. To initiate the continuous read after the COM port is opened, we will need to set the maximum number of characters read at each call to:

`n = 0.1 / ((1 + 8 + 1 + 0) / 9600) = 96 characters`

The following line of code can be placed just after the COM port is opened:

| `// Read 96 max characters each time` `oSerialCommRef.startContinuousRead( 96, this.processRxData )`

At this point, the function `processRxData()` of the form will be called only if new characters have arrived on the line.

Note that the number of characters, 96, must be lower than the default Rx queue size, in general 4096, to meet requirement (4).

To stop a continuous read, the `stopContinuousRead()` method can be invoked without parameters. It will stop the timer and any further read from the COM port.

- Writing to the communication port

As opposed to the read process, the write process (and this is only a design choice) will be synchronous. We could alternatively have set a Tx queue in order to make the transmit process asynchronous, so that the characters to transmit would be placed in this Tx queue and wait there to be transmitted. This was not our design choice for two reasons:

- The fact that the application stops and waits until the characters to send have been effectively transmitted is not undesirable,
- If an error occurs during transmission, then the application can know it immediately as the return from the call will indicate an error.

Since we’ll be using synchronous transmission, the Tx queue’s length is set (by default also in Windows) to 0.

Now, writing a string of characters to the COM port from Visual dBASE can be done using one of two methods of the SerialComm component:

- The first method, `writeString()`, will send the string provided as an input parameter in one block. From an application point of view, the consequence is that no read can be performed during this operation, even if a continuous read is previously started, because Visual dBASE’s timer ticks cannot interrupt a Windows API function. So while this function can be used to send characters, it should not be used in situations where characters are read using the `startContinuousRead()` process.

- The second method,`fullDuplexWrite()`, has been designed especially to deal with the situation where a read using `startContinuousRead()` must be performed while transmitting characters. So for full duplex mode, this method will:

- stop the continuous read
- split the string to send, provided as parameter, into substrings of length n, the same n as for `startContinuousRead()`,
- alternatively:

1. Send each substring.
2. If the continuous read was enabled at the time `fullDuplexWrite()` was called, read the COM port. If characters are available, then call the user ISR to process them.

- After the last substring is sent, place the continuous read in the same state it was before the call to `fullDuplexWrite().`

By adopting these steps, the requirement to read and write data at the same time can be met. As `fullDuplexWrite()` can be used in any situation, it is thus preferable to use it rather than `writeString()`.

- Dealing with communication events

Communication events will be classified into two types:

- Reception line errors can originate from hardware. For instance, electromagnetic signals, and, more generally, interferences can generate noise and jam the serial line signals. This situation can be observed, for instance, if the cables are long, are running at high speed, and lie in the vicinity of electrical systems.

- Protocol events will influence the transmission of data in certain situations. Such is the case if the host receiver is not ready to accept new data.

These two types of events can be trapped with the SerialComm component.

- Reception line errors can be checked using the `checkCommErrors()` method of SerialComm.

- In case no errors have occurred, this method does not update any error information, and simply returns true, meaning no error occurred.
- If an error occurred, the method will update the`.CommErrors` property of the component, which contains two arrays that will be filled with the types of errors that occurred. It will also reset these error flags.

As a general rule, it is recommended to check, after each reception of characters, the reception line errors by issuing a call to the `checkCommErrors()` method. This also has the effect of resetting the reception error flags.

The errors that can be reported are the following:

Error text string set in .CommErrors.text | Error type set in .CommErrors.type | Meaning
Break | 1 | A break signal has been detected on the line
Framing | 2 | An incorrect character framing was detected
Char overrun | 3 | A char overrun occurred due to incorrect stop bits
Parity | 4 | The parity check was unsuccessful
Transmit overflow | 5 | This case cannot actually happen, as no transmit buffer is used.
Receive overflow | 6 | The receive buffer is full, and new characters will be lost. This can occur if no protocol is set, or if no read orders are issued and characters arrive on the line.

- Protocol events and port status
The protocol events will influence the behavior of the transmit task of both the receive and the transmit process. According to the protocol set using the `SetHandshake()` method, different behaviors are possible when a write to the COM port is requested:

1. No protocol set
In this case, it is always possible to transmit characters on the line, whatever the state of the status lines. There will be no errors reported from any write action.
2. Xon/Xoff handshake
For this protocol, writing to the COM port will not be allowed if either:

- An Xoff character is received, meaning that we are requested to stop further transmission. Further transmission will be enabled as soon as the Xon character is received.
- An Xoff character is sent, meaning that the input queue has filled up and has triggered the Xoff character. Although stipulated in the API OLH that the choice is given to enable or disable transmissions when the Xoff is sent, Windows 95 seems to ignore such a condition and always disables further transmissions until the Xon is sent. That is, when the input queue has been emptied down to the limit that triggers the Xon transmission.

Before writing to the COM port, It is always possible to know whether transmission will be allowed, by calling the `checkPortStatus()` method. This method will update the`.PortStatus` property of the component, which contains itself two useful properties for dealing with Xon/Xoff protocol:

- The`.PortStatus.XoffHold` tells, if`true`, that we are in the first above case, an Xoff has been received, and we are expecting now an Xon to be received in order to enable transmissions again.
- The`.PortStatus.XoffSent` indicates, if `true`, that we are in the second situation, an Xoff has been sent, and now we are waiting for the input queue to be emptied enough (by reading it) so that the Xon can be sent and the flag reset.

If we attempt to write to the COM port when the transmission is disabled, the `writeString()` or `fullDuplexWrite()` methods will return `false`, meaning that transmission was not allowed, or a stop situation occurred during the transmission of the string passed as parameter. In that case, it is possible to know how many characters have not been sent, by checking the`.PortStatus.CharsNotSent` property of the component. When transmission is enabled again, the characters not sent must be written again to the port.
3. Hardware protocols
A Hardware protocol is set when choosing either the RTS/CTS, the DSR/DTR , or both of them. With these protocols, writing to the communication port will be disabled if:

- The CTS signal is not asserted, and the RTS/CTS protocol is selected.
- The DSR signal is not asserted, and the DSR/DTR protocol is selected.

As for the Xon/Xoff protocol, before writing to the COM port, it is always possible to know whether transmission will be allowed, by calling the `checkPortStatus()` method. This method will update the`.PortStatus` property of the component, which contains two useful properties dealing with hardware protocol:

- The`.PortStatus.CTSHold` gives the status of the CTS signal. If `true`, the signal is not asserted.
- The`.PortStatus.DSRHold` gives the status of the DSR signal. If `true`, the signal is not asserted.

As for the Xon/Xoff protocol, attempting to write to the COM port when the transmission is disabled, using the `writeString()` or the `fullDuplexWrite()` methods, will lead these methods to return `false`.This means that transmission was not allowed, or a stop situation occurred during the transmission of the string passed as a parameter. In that case, it is possible to know how many characters have not been sent, by checking the`.PortStatus.CharsNotSent` property of the component. When transmission is enabled again, the characters not sent must be written again to the port.

It is always possible to ask for the port status in order to get the state of the above flags by using the `checkPortStatus()` method. Another benefit of calling that method is that it will reset the communication errors, if any.

- Closing the communication port

A communication port is handled by Windows like any other file on your system. When you open a file, you need to close it or you won’t be able to access it, unless you close your application. That’s why a communication port needs also to be closed after it has been used. This is easily achieved by simply calling the `closePort()` method of the component:

| `oSerialCommRef.closePort()`

where `oSerialCommRef` is the SerialComm component’s reference.

- Miscellaneous functions

Some miscellaneous features have also been added to perform some additional tasks:

- The`.displayMessages` property
This property, set to `true` by default, enables the component to display its internal error messages. Turning this property to `false` will disable these messages, but you can always know the result of any method by testing its return code.
- The `sendbreak()` method
This method will send a break signal on the transmission line for a default duration of 1 second. You can supply your own duration, as an input parameter of the `sendbreak()` method.
- The `flushPort()` method
This method will flush the receive and transmit queues, deleting all the characters they contain.

An application of the SerialComm 1.21 component: a mini TTY console

After having overviewed all the SerialComm component’s possibilities, let’s now turn to an application that uses this component. We’ll go over the main implementation techniques needed to get the best efficiency and results from it.

The application proposed here is a mini TTY console, which is a kind of terminal emulator that will be enabled to receive and send data in full duplex mode, and display this data in an editor window. This may remind you in some way the “hyperterminal” application that ships with Windows. Its name is`TTYdemo.wfm`, and it is delivered as part of the SerialComm 1.21 package. (See the link at the end of this article).

This form has two pages. The first one, shown on startup, presents a screen editor which is used both to display the incoming data from the serial port and to enter data to be sent to the same port. The following image presents an example of a session which was obtained while connecting to an ISP provider through a modem. The commands entered on the keyboard are:

- An ATI command, that makes the modem return its identification string
- An ATDT command, followed by the phone number to dial. In that case, the modem itself will grab the line and dial the number. After that, the protocol is established between the ISP and the modem, leading to the “CONNECT 33600” message sent by the modem. The host then prompts for the login name and password.

In addition to the screen editor, the top of the window contains a set of comboBoxes to choose the communication port and physical line parameters, and a red light that turns to green if the communication port is successfully opened. The bottom of the window has some buttons for specific actions:

- The Send BREAK button will send a .5 s break signal on the line.
- The Echo OFF toggle button enables or disables the local echo of keystrokes to the editor,
- The Clear Window button erases the editor.
- The Errors and Status button will display the second page of the form. The second page summarizes all the port status and reception error counts for the current session.

Clicking on the Errors and Status button will display the following screen:

On the second page, the Received Errors box contains a set of received error counters. The Receive Status box indicates if an Xoff has been sent as well as the count of unread characters in the receive queue. A Transmit Errors and Status box tells if the transmission is possible or blocked, and, in that case, the number of unsent characters. A set of buttons will also perform:

- the TTY Window button will display the form’s first page,
- the Update Status button manually updates the current status of the port,
- the Reset error counts button resets all the error counters to zero.

All these components are set up with standard Visual dBASE techniques, and we will not get into more detailed explanations here. On the other hand, we will take an in-depth look at how the different steps described in the Using the SerialComm 1.21 component chapter are used here in this typical communication application.

- Opening the communication port and initiating the read process.

This first part is accomplished in the form’s onOpen event with the following piece of code:

| `685 // Linked method: form's onOpen event` `686 Function form_onOpen` `687 local n, pSC` `688 pSC = this.serialComm1` `689` `690 this.text = "TTY demo -- " + pSC.version // set title` `691` `692 // Disable component's error messages` `693 pSC.displayMessages := false` `694` `695 // opens selected COM port, at 9600 baud, parity None, 8 data bits, 1 stop.` `696 this.bOpened = pSC.openPort( this.CB_PORT.value, ;` `697 val( this.CB_SPEED.value ), ;` `698 this.CB_PARITY.value, ;` `699 val( this.CB_DATA.value ), ;` `700 val( this.CB_STOP.value ))` `701` `702 // Compute the number of chars to be read each time by the` `703 // continuous read at a 100 ms period rate.` `704 n = 0.1 * pSC.BaudRate / ;` `705 ( 1 + pSC.Data + pSC.StopBits + iif( pSC.Parity == "None", 0, 1 ))` `706` `707 // starts a continuous read for 50 characters, and update the control light.` `708 this.bStartContRead = pSC.startContinuousRead( n, this.newCharsReceived )` `709 this.setLight( this.bOpened and this.startContRead )` `710 return`

Line 690 uses the`.version` property of the component to display its version.

Line 693 disables the internal error messages of the component since this app will use the component’s return codes to determine if an error occurred.

Line 696 opens the communication port, given the physical line parameters and port number selected using the comboBoxes. As we will see further on, each time a comboBox is changed, this onOpen method will be executed again.

Line 704/705 computes the number `n` of characters to read from the receive buffer in the continuous read process so that the read period is 100 ms.

Line 708 will begin the continuous read process, each time getting an expected maximum of `n` characters and using the ISR callback function `this.newCharsReceived()`

Using the `openPort()` and `startContinuousRead()` return codes, line 709 will turn the light to green (if true) or red (if false).

- Setting the protocol handshake

The protocol comboBox will call the following code when it is changed:

| `764 // Linked method : Combobox Handshake CB_HDSK onChange event` `765 Function CB_HDSK_onChange` `766 local nHandshake` `767 do case` `768 case this.value == 'No Handshake'` `769 nHandshake = 0` `770 case this.value == 'Xon/Xoff'` `771 nHandshake = 1` `772 // sets an Xon/Xoff config : Xon, Xoff, 25%/85%, Rx buffer = 2000 bytes` `773 this.parent.serialComm1.setXonXoffConfig( 0x11, 0x13, 25, 85, 2000 )` `774 case this.value == 'DTR/DSR+RTS/CTS'` `775 nHandshake = 2` `776 case this.value == 'DTR/DSR only'` `777 nHandshake = 3` `778 case this.value == 'RTS/CTS only'` `779 nHandshake = 4` `780 endcase` `781 this.parent.setLight( this.parent.bOpened and this.parent.bStartContRead and ;` `782 this.parent.serialComm1.setHandshake( nHandshake ))` `783 this.parent.updateStatus( ) // Update the status panel` `784 return`

According to the protocol comboBox value, lines 767 to 780 will determine the corresponding handshake code (0 to 4) and set an Xon/Xoff configuration.

Line 781 will try to set this handshake type. The light will reflect the execution status of the protocol change.

Line 783 will force the update of the Error Status screen. In the case of a hardware handshake selected, this can report immediately a transmit hold condition, if, for instance, the expected hardware signals (CTS or DSR) are not present.

- Changing the port’s parameters

The five comboBoxes (`CB_PORT, CB_SPEED, CB_PARITY, CB_STOP,` and `CB_DATA`) dedicated to choosing the port parameters, will call the `onChange` event routine of the similarly-named ComboBox:

| `752 // Linked method: ComboBox for COM port selection` `753 Function CB_PORT_onChange` `754 this.parent.canClose( ) // Closes the current port if it's open` `755 this.parent.onOpen( ) // Try to open the new COM port.` `756 this.parent.CB_HDSK.onChange( ) // Set the selected handshake` `757 return`

> First, the port is closed by calling the form’s `canClose` event. Then it is reopened by calling the form’s `onOpen` event. When reopening the communication port, the new line parameters selected will be taken into account. Line 756 also sets up the selected handshake method.

- Reading data from the communication port

When the port is successfully opened, the onOpen event (as seen above) initiates a continuous read process, calling regularly the `newCharsReceived()` method. Let’s have a look on what this `newCharsReceived()` method is doing:

| `812 // User-defined Interrupt Service Routine` `813 Function newCharsReceived( cReceivedString )` `814 local sDisplay, i, c, nC` `815 sDisplay = ''` `816 // Parse the received string and replace the control chars to '^ + Letter',` `817 // except CR and LF, so that they are displayed in the editor window.` `818 for i = 0 to cReceivedString.length - 1` `819 c = cReceivedString.charAt( i )` `820 nC = asc( c )` `821 sDisplay += iif( nC < 32 and nC <> 13 and nC <> 10, '^' + chr( nC + 64 ), c )` `822 endfor` `823` `824 this.parent.ED_EDITWNDW.value += sDisplay // Now, display the string,` `825 this.parent.ED_EDITWNDW.keyboard( '{Ctrl+End}' ) // scroll to the bottom end,` `826 this.parent.updateErrors() // and check for receive errors.` `827 return`

First, this method receives a parameter, here `cReceivedString`. Each time this method is called, it will receive the new characters in this parameter string.

Then, lines 818 to 822 will parse the received string to see if control characters have been received. Control characters have an ASCII code below 32, and cannot be displayed directly. We will display them using the following code:

`chr(1) will be displayed ^A`
`chr(2) will be displayed ^B`
`...`
`chr(31) will be displayed ^_`

Lines 824 and 825 then display the processed string in the editor window and scrolls to the bottom.

The next line, 826, will check if reception errors occurred. Reception errors should be checked each time characters are received. The description of the `updateErrors()` method follows.

- Writing to the communication port

This application is designed so that each time a new character is entered on the keyboard, it will be sent immediately to the communication port. For instance, if the application was designed to send a file, it would be better to send strings with several characters at a time, so that it is faster, i.e., less time is lost between each character. At the most, a complete file stored in a string can be sent as a whole, the full duplex mode being entirely managed by the component itself. The characters to send are processed here by the editor’s key event.

| `786 // Linked method: Editor's key event` `787 // According to the PB_ECHO state and the key pressed, the display will be` `788 // adjusted. The char will be sent on the line here. Transmission error is` `789 // updated here.` `790 Function ED_EDITWNDW_key(nChar, nPosition, bShift, bControl)` `791 if this.parent.PB_ECHO.value // display chars in Echo on mode only` `792 if nChar == 13 or nChar == 10 // CR or LF` `793 elseif nChar < 32` `794 this.value += '^' + chr( nChar + 64 )` `795 this.keyboard( '{Ctrl+End}' )` `796 else` `797 this.value += chr( nChar )` `798 this.keyboard( '{Ctrl+End}' )` `799 endif` `800 endif` `801 if this.parent.bOpened` `802 // transmit the char to the serial line` `803 if not this.parent.serialComm1.fullDuplexWrite( chr( nChar ) )` `804 // Here, an error occurred during transmission. The char was not sent.` `805 // The Total unsent chars count is updated` `806 this.parent.EN_UNSERR.value++` `807 this.parent.updateStatus( )` `808 endif` `809 endif` `810 return 0`

We first check lines 791 to 800 to see if the local echo is enabled by the Echo ON/OFF pushbutton, and if a control character has been entered on the keyboard. If so, it is displayed using the same rules as the incoming characters that are received from the communication port. A control character can be entered on the keyboard using the combination Ctrl + <Letter> :

`Ctrl-A will enter a chr(1)`
`Ctrl-B will enter a chr(2)`
`...`

Then, the character is sent on the communication port (line 803) using the `fullDuplexWrite()` method. This method performs the transmission of data, while still allowing the reception of incoming data. Note that because we send only one character at a time, we could also have used the `writeString()` method. Sending a single character is performed in much less time than the receiving timer tick, which is set here to a duration of 100 ms.

On line 806, if an error occurred during transmission (`fullDuplexWrite()` returned `false`), the count of unsent characters is updated by one, and the `updateStatus()` method is called to update the Error and Status screen.

This function returns 0 (line 810) because the character must not be displayed. This is done directly inside the function (as seen above).

- Dealing with communication events and getting the port status

We have just seen that the read and write functions used in this application were calling two particular methods, named `updateErrors()` and `updateStatus(),` in order to check for communication errors (after each read) and the port status (after each write). Let’s focus now on those two functions.

1. The `updateStatus()` function, called when a write to the communication port returned `false`. This function is detailed hereunder. The main action appears in line 721, where, as stated before, the call to the `checkPortStatus()` method of the SerialComm component will first reset the reception errors, if no new errors are present. Then it will set the`.PortStatus` property of the component according to the communication port status.

Lines 722 to 726 simply update the display with the new status.

| `719 // This method updates the status fields of the status and errors panel.` `720 function updateStatus` `721 this.serialComm1.checkPortStatus( )` `722 this.CH_DSRHLD.value := this.serialComm1.PortStatus.DSRHold` `723 this.CH_CTSHLD.value := this.serialComm1.PortStatus.CTSHold` `724 this.CH_XOFFHLD.value := this.serialComm1.PortStatus.XoffHold` `725 this.CH_XOFFSNT.value := this.serialComm1.PortStatus.XoffSent` `726 this.EN_CHRXQ.value := this.serialComm1.PortStatus.CharsInRxQ` `727 return`

2. The `updateErrors()` function, called after each read, checks for read errors. This function is listed below. Here also, the key point (line 732) consists of a call to the `checkCommErrors()` method of the SerialComm component.

- If no error occurred (`checkCommErrors()` returns `true`), nothing is performed, so very little time is taken for this operation.
- In the opposite case (`checkCommErrors()` returns `false`), an update (lines 733 to 747) of the error counters is performed on the Errors and Status page. For that, the`.CommErrors.type[]` array is scanned, and errors types encountered updated.

Then, the reception errors are cleared (line 748) with a call to `updateStatus()`. Clearing the reception error flags is important. Otherwise, you would get a previous error type reported each time a new error occurs.

| `729 // This method updates the receive error counts and status of the status and errors panel.` `730 Function updateErrors` `731 local i` `732 if not this.serialComm1.checkCommErrors( ) // test if errors have occurred` `733 for i = 1 to this.serialComm1.CommErrors.type.size` `734 do case` `735 case this.serialComm1.CommErrors.type[ i ] == 1` `736 this.EN_BRKCNT.value++` `737 case this.serialComm1.CommErrors.type[ i ] == 2` `738 this.EN_FRMERR.value++` `739 case this.serialComm1.CommErrors.type[ i ] == 3` `740 this.EN_OVRERR.value++` `741 case this.serialComm1.CommErrors.type[ i ] == 4` `742 this.EN_PARERR.value++` `743 case this.serialComm1.CommErrors.type[ i ] == 5` `744 case this.serialComm1.CommErrors.type[ i ] == 6` `745 this.EN_ROVERR.value++` `746 endcase` `747 endfor` `748 this.updateStatus() // To clear the reception errors, if any` `749 endif` `750 return`

- Closing the communication port

This action will be triggered if:

- The TTYdemo form is closed by clicking in the close [x] button, or,
- If the port’s parameters are changed in such a way that it is necessary to close it and reopen it to take the new parameters into account.

By closing the communication port in the form’s `canClose` event, the port will be closed when the form is closed, or when requested explicitly by a direct call to this method.

| `712 // Linked method: form's canClose event` `713 Function form_canClose` `714 if this.bOpened // If a port is opened,` `715 this.serialComm1.closePort( ) // close the port before exit` `716 endif` `717 return true`

- Miscellaneous functions

The TTYdemo application is also able to send communication Break signals. This is accomplished by the Send BREAK pushbutton. The onClick event of this pushbutton is simply linked to a codeblock that will perform this action. The form’s constructor code contains the following lines:

| `110 this.PB_BREAK = new PUSHBUTTON(this)` `111 with (this.PB_BREAK)` `112 onClick = {;this.parent.serialComm1.sendBreak( 0.5 )}` `113 height = 24` `114 left = 10` `115 top = 349` `116 width = 96` `117 text = "Send BREAK"` `118 fontSize = 9` `119 fontBold = true` `120 value = false` `121 endwith`

Line 112 will send a break signal of 0.5 seconds when the button is clicked.

Conclusion

RS232, as a serial communication standard available on all PCs, still remains a good alternative for exchanging point to point information between computers or equipment.

Designing a software application that handles the serial communication ports has never been an easy task, even during the DOS age, as the algorithms necessary to set up have to cope with external hardware events. Moreover, timing aspects involved in such applications often requires notions of real time software programming.

Windows has now slightly changed this landscape, as it manages and controls all the hardware resources of the machine. In fact, it has masked some principles using intermediate software layers, but the basics still remain the same.

However, its kernel has opened a way to design low level like communication applications with high level development tools such as Visual dBASE. Using its high level programming language, focus can be turned to the design and conception aspects of the project. These are of prime importance in reaching a satisfactorily and efficient result.

To finish this article, I would like to say a few words about the SerialComm project. It started 1-1/2 years ago, as I needed to design an application that could connect and download data from a server through a modem and a telephone line. The first version of this component (1.01) was issued in September 1998.

At this time, I noticed that many people on the Borland newsgroup were seeking a similar tool, so I decided to share this already-designed code.

It already included many functions. But some basic areas, like status and errors report, handshaking, were absent or rather weak. As I needed to improve this part of the component for better robustness against non nominal cases in that application, I achieved a second version of it, SerialComm 1.2, and then 1.21 while writing this article.

Additionally, after the first release of the component, I received some feedback and questions, sometimes dealing with elementary notions on operating the serial communications. I hope this article, which can be used as a general guideline on the subject, will be able to come up to those expectations.

Acknowledgments: I would like to thank especially Flip Young for her advice and their kind contribution to the proofread of this article.

To download the SerialComm 1.21 package, including the demo program, click here
(It’s a 35 Kb file)