Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to create serial port events?

1,673 views
Skip to first unread message

Philip

unread,
May 8, 2008, 3:30:11 PM5/8/08
to
Hi, I can read from the serial port when triggered by an event, for example from a button click:-

procedure TForm1.ReceiveButton(Sender: TObject);
//Runs when Button4 is clicked
begin
Button4.Enabled := False;
Memo2.Text := ReceivePacket();
Button4.Enabled := True;
end;

function TForm1.ReceivePacket(): String;
//Dumps receive buffer to string
var
d: array[1..80] of Char;
s: String;
BytesRead: Cardinal;
i: Integer;
begin
s := '';
if ReadFile (ComFile, d, sizeof(d), BytesRead, Nil) then
for i:= 1 to BytesRead do s := s + d[i];
Result := s;
end;

This is easy for a newbie because I can create the event in the IDE in the Button4 properties
But...
How do I create an event from the serial port itself?
According to the Microsoft documents I should be able to create an event mask for EV_RXCHAR and then
replace the procedure TForm1.ReceiveButton(Sender: TObject) above with an event handling procedure
that runs everytime a packet(s) arrives at the serial port.
I know that I have to read the number of packets in the buffer (maybe using ClearCommError) and that
kind of thing, but it's the leap in Delphi understanding that I can't past for writing my own event
and making it fire an event handling procedure that I need help with.

I've read a few bits and pieces and some books about this, but I just can't get my head round it.
I'm using Turbo Delphi Explorer free edition by the way.

Any help would be appreciated.

Thanks, Philip

John Herbster

unread,
May 8, 2008, 4:02:20 PM5/8/08
to

"Philip" <su...@christiantena.net> wrote
> ...

> function TForm1.ReceivePacket(): String;
> //Dumps receive buffer to string
> var
> d: array[1..80] of Char;
> s: String;
> BytesRead: Cardinal;
> i: Integer;
> begin
> s := '';
> if ReadFile (ComFile, d, sizeof(d), BytesRead, Nil) then
> for i:= 1 to BytesRead do s := s + d[i];
> Result := s;
> end;
> ...

> Any help would be appreciated.

Philip,
I suggest using a COM-port component like Dejan Crnila's
TComport from
http://sourceforge.net/projects/comport/
Then you can read and accumulate the characters as they
come in inside an OnRxChar event handler. Then from you
button event handler, you can fetch the accumulated characters
and empth the array.
HTH, JohnH

Philip

unread,
May 8, 2008, 4:15:19 PM5/8/08
to
You can't use third party components with Delphi Turbo Explorer free edition.
Anyway I can open a port, close a port, send packets and receive packets.
If I could just figure out how to write my own events then I would be there.
Writing your own events is a kind of generic question I think anyway.

regards, Philip

Remy Lebeau (TeamB)

unread,
May 8, 2008, 4:20:17 PM5/8/08
to

"Philip" <su...@christiantena.net> wrote in message
news:48235f15$1...@newsgroups.borland.com...

> You can't use third party components with Delphi Turbo Explorer free
> edition.

Yes, you can. You just can't install them into the IDE for use at
design-time. But you can instantiate them at runtime. In the case of
open-source components, you could add the source code directly to the
project if you don't want to use packages.

> If I could just figure out how to write my own events then I would be
> there.

Microsoft has detailed documentation on how to handle port events, with code
snippets (that you could translate into Pascal):

http://msdn.microsoft.com/en-us/library/ms810467.aspx


Gambit


John Herbster

unread,
May 8, 2008, 9:26:22 PM5/8/08
to
>> I suggest using a COM-port component like Dejan Crnila's
>> TComport ...

"Philip" <su...@christiantena.net> wrote


> You can't use third party components with Delphi Turbo
> Explorer free edition.

If you take Remy's advice to "instantiate them at runtime",
it is really not difficult. You just *include* the TComport
source modules and place the module names in the appropriate
*uses* clauses. Then at runtime, maybe in the OnFormCreate,
You instantiate the TComport objects by doing something like
Comport1 := TComport.Create(self);
Then you have to set each of the Comport properties according
to your programs needs. That includes creating your own
OnRxChar event handler and assigning it to
Comport1.OnRxChar := <your OnRxChar event method name>
HTH, JohnH

Martin James

unread,
May 9, 2008, 2:29:16 AM5/9/08
to

"Philip" <su...@christiantena.net> wrote in message
news:48235482$1...@newsgroups.borland.com...

> Hi, I can read from the serial port when triggered by an event, for
> example from a button click:-
>

If you want to fire a 'SerialRxData' event, this is 'normally' done by a
synchronized call from a read thread through an event property. A read
thread is used because of the blocking nature of the WaitCommEvent API when
overlapped I/O is not used. This works OK, (mostly).

A better alternative is to actually use overlapped I/O and completion
routines - one thread can then receive and transmit bufferfulls of data and
detect comm events from many serial ports 'at the same time'. Main-thread
events can be fired with buffer class instance parameters from a window to
which your serial thread posts messages. *Don't try this at home* <g>

Unless you want to try for a homebrew solution using synchronized calls, or
are happy with overlapped I/O, multiThreading etc, and need very high
performance and can put up with lots of debugging, follow the advice of
others and create an instance of a serial port component that already works.

Rgds,
Martin

Philip

unread,
May 9, 2008, 4:41:05 AM5/9/08
to
I accept that using the comport library is probably the way to go, but
I'm still curious, and I'm enjoying messing about with this for the time
being...

I haven't quite figured out what the problem is with nonoverlapped I/O.

I understand that you can't send and receive at the same time. That
might be a problem if you are running a networking protocol over serial,
but if you are talking to a device that has some kind of command /
response type protocol then it wouldn't be a problem would it?

Even if a byte did arrive whilst you are transmitting it doesn't get
lost or cause a delay will it? It just means that it gets buffered by
hardware/OS and we can't read from the buffer until we finish transmitting.

The biggest problem seems to be waiting for bytes to arrive and
commtimeouts etc, however if we have an event to tell us that bytes are
in the buffer then there's no wait.

i presume that if I am transmitting bytes and one arrives then the event
handler will be triggered to read bytes, but will be blocked until the
transmit has finished, and then it will execute fine; is that right? I
think I could live with that.

thanks, Philip

Philip

unread,
May 9, 2008, 10:13:42 AM5/9/08
to
I tried what John suggested, and it does work, but with one small hicup
(see below). Expanding what John said, this is how you do it:-

1. Unzip the ComPort sources onto your hard drive
2. In your project select Project/Add to Project..
select CPort.pas from the ComPort sources
Repeat for the other seven .pas files
3. Under type add "ComPort1: TComPort;"
4. In your project Object Inspector/Events create an event handling
procedure for event OnCreate.
5. To this procedure add "ComPort1 := TComport.Create(self);", example:-

procedure TForm1.FormCreate(Sender: TObject);
begin
ComPort1 := TComport.Create(self);
end;

6. To make a procedure that receives packets make something like this:-

procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
Str: String;
begin
ComPort1.ReadStr(Str, Count);
myreceivedpackets := Str;
end;

7. To run that procedure when a packet arrives do something like this:-

ComPort1.CustomBaudRate := mybaudrate;
ComPort1.Open;
ComPort1.OnRxChar := CommPort1RxChar;

Now some questions...

When I compile my program I get this error message:-
"Field Form1.ComPort1 does not have a corresponding component. Remove
the declaration? Yes/No/Cancel"
If I click "no" then the program compiles and runs fine.
What is causing this? How can I fix it?

My procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer)
requires an integer to be passed to it telling the routine how many
packets to read from the serial port buffer. The program is working
fine and the correct number is passed, but I'm not sure how setting
ComPort1.OnRxChar := CommPort1RxChar achieved this. It almost feels
like the statement should be the other way round too.

Going from a program that already worked in Delphi 7 with the ComPort
installed in the IDE seems quite easy. However doing all this from
scratch for the first time in Turbo Delphi Explorer might be a bit
challenging without some clear examples or instructions.

thanks, Philip

John Herbster

unread,
May 9, 2008, 10:37:18 AM5/9/08
to
"Philip" <su...@christiantena.net> wrote

> 3. Under type add "ComPort1: TComPort;"

I think that "ComPort1: TComPort" should be declared in the
"private" section of your form's declaration. Also note that I
have move some lines around:

> ...
private
ComPort1: TComPort;
MyReceivedPackets : string;
procedure ComPort1RxChar(Sender: TObject; Count: Integer);
> ...
ComPort1 := TComport.Create(self);
ComPort1.CustomBaudRate := mybaudrate;
ComPort1.OnRxChar := CommPort1RxChar;
ComPort1.Open;
> ...


procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var Str: String;
begin
ComPort1.ReadStr(Str, Count);

MyReceivedPackets := MyReceivedPackets + Str;
end;



> What is causing this? How can I fix it?

I think that moving the variable to the private section will fix that.

HTH, JohnH

Martin James

unread,
May 9, 2008, 10:52:56 AM5/9/08
to

"Philip" <su...@christiantena.net> wrote in message
news:48240e23$1...@newsgroups.borland.com...

>I accept that using the comport library is probably the way to go, but I'm
>still curious, and I'm enjoying messing about with this for the time
>being...
>
> I haven't quite figured out what the problem is with nonoverlapped I/O.

You can't send and receive at the same time in the same thread.

> I understand that you can't send and receive at the same time. That might
> be a problem if you are running a networking protocol over serial, but if
> you are talking to a device that has some kind of command / response type
> protocol then it wouldn't be a problem would it?

It's not a serios problem anyway - a sperarate receive thread works OK in
non-overlapped mode.

> Even if a byte did arrive whilst you are transmitting it doesn't get lost
> or cause a delay will it?

It will not normally get lost, no.

It just means that it gets buffered by
> hardware/OS and we can't read from the buffer until we finish
> transmitting.

In many cases, this is not a problem, true.

> The biggest problem seems to be waiting for bytes to arrive and
> commtimeouts etc, however if we have an event to tell us that bytes are in
> the buffer then there's no wait.

'Event' means different things in different contexts <g>

An event in Delphi is often accepted to be an event fired in the main thread
by a message. A comm event is someting that has to be waited for., The
wait is blocking, and any user thread that calls WaitCommEvent in
non-overlapped mode can expect to be useless for any other purpose.

> i presume that if I am transmitting bytes and one arrives then the event
> handler will be triggered to read bytes, but will be blocked until the
> transmit has finished, and then it will execute fine; is that right? I
> think I could live with that.

The main problem is that a thread calling readFile in non-overlapped mode,
or waitCommEvent in non-overlapped mode, will be blocked until characters
arrive. This means that if you transmit a protocol unit and then call
readFile/waitCommEvent from your main thread, your main thread will get
stuck if the requested data is not received.

To retain sanity, either use overlapped I/O, (preferably in a secondary
thread), or non-overlapped I/O and definitely a secondary read thread.

Rgds,
Martin


Philip

unread,
May 9, 2008, 10:56:46 AM5/9/08
to
You are right.
I moved it to private. The error messages have gone.

thanks, Philip

Philip

unread,
May 9, 2008, 12:40:28 PM5/9/08
to
Just in case anyone else needs this...

When you do Project/Add to Project you only need to add six of the eight
.pas files in the Comport sources
these are :-
CPort.pas
CPortCtl.pas
CPortEsc.pas
CPortSetup.pas
CPortTrmSet.pas
gnugettext.pas

if you try to add cportreg.pas then it won't compile, and CPortAbout.pas
isn't necessary, at least not in my program

regards, Philip

Remy Lebeau (TeamB)

unread,
May 9, 2008, 12:39:56 PM5/9/08
to

"Philip" <su...@christiantena.net> wrote in message
news:48245c17$1...@newsgroups.borland.com...

> ComPort1.Open;
> ComPort1.OnRxChar := CommPort1RxChar;

You should be assigning the event handler before opening the port, not
afterwards.

> When I compile my program I get this error message:-
> "Field Form1.ComPort1 does not have a corresponding component.
> Remove the declaration? Yes/No/Cancel"
> If I click "no" then the program compiles and runs fine.
> What is causing this? How can I fix it?

Your form's DFM has a reference to a TComPort object that is not declared in
the form class's "published" section. You can either let the IDE remove the
reference from the DFM for you, and continue using the OnCreate event to
instantiate the TComPort manualy. Or else simply move your TComPort
declarating into the published section, rename it to match the object Name
in the DFM, get rid of the OnCreate handler altogether, and let the RTL
handle the rest for you at runtime.

> My procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer)
> requires an integer to be passed to it telling the routine how many
> packets to read from the serial port buffer. The program is working fine
> and the correct number is passed, but I'm not sure how setting
> ComPort1.OnRxChar := CommPort1RxChar achieved this.

You are assigning an event handler. The TComPort component calls the event
handler automatically whenever data arrives on the port.

> Going from a program that already worked in Delphi 7 with the
> ComPort installed in the IDE seems quite easy.

That is why your DFM still had an old reference in it. You migrated an
existing project from an earlier IDE version.


Gambit


bugrak...@gmail.com

unread,
Mar 20, 2015, 7:25:49 PM3/20/15
to
8 Mayıs 2008 Perşembe 22:30:11 UTC+3 tarihinde Philip yazdı:
Hello,
How to wait for COM port receive event before sending more data in a loop?
IS there a way? I use the same component and I am fine sending and reading it with buttons. But I want the system to listen if a barcode scanned and it will trigger the action and then wait for other barcode to be scanned?
Thanks
0 new messages