Re: rs232 -help!
Hello Heinz,
Okay, I never really found out how to set the comm timeout, however I got it
to work with the following code. I can read the port and the Read functon
will fill the buffer and return. I will then store it in a back-up-buffer
which will hold the expected data. Next I increment a variable so to never
come back to the back-up-buffer while Read returns any subsequent junk (It
returns junk for one or two iterations), so I just ignore this. When finished
reading junk, it breaks out of the loop, and the next message will obviously
not be related to an rs232 event and so, we reset the counter value, hence
the Read function will now be ready for the next rs232 event.
See the code below:
============================================
//Global object declared one time
CSerialWnd MySerial;
LRESULT CALLBACK WndProc_CW1 (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static DWORD dwBytesRead = 0;
static BYTE abBuffer[8];
static TCHAR szBuffer[17];
static int LoopCount = 0;
//Serial port monitor
if (message == CSerialWnd::mg_nDefaultComMsg)
{
// A serial message occurred
const CSerialWnd::EEvent eEvent = CSerialWnd::EEvent(LOWORD(wParam));
const CSerialWnd::EError eError = CSerialWnd::EError(HIWORD(wParam));
MySerial.Setup(CSerial::EBaud9600,CSerial::EData8,
CSerial::EParNone,CSerial::EStop1);
switch (eEvent)
{
case CSerialWnd::EEventRecv:
do
{
MySerial.Read(abBuffer,sizeof(abBuffer),&dwBytesRead);
if (LoopCount == 0){
MultiByteToWideChar(CP_ACP,0,
(LPCSTR)abBuffer,8,szBuffer,17); //Store in back up buffer
LoopCount ++;}
else
{/*Do nothing*/}
}while(dwBytesRead == sizeof(abBuffer));
break;
default:
break;
}
return 0;
}
else
LoopCount = 0;
....other code.....
switch(message)
{
case WM_CREATE:
MySerial.Open(TEXT("COM1"),hwnd,WM_NULL,lParam,0,0);
....other code....
case WM_CLOSE:
MySerial.Close();
.... other code ...
}
=====================================================
It never hangs anymore, and the data gets read in one whole string and any
size aswell. Actually I haven't tested how many successive bytes I can read
at once, but I know that I have done many tests, and I can read one to 8
bytes no problem, I will try to increase the for loop in the controller so to
send 100 bytes, I don't think it will a problem!
Anyhow, this has been a tough one, and I thank all the fellows who
contributed to my rs232 dilema. I don't know if I will run into problems with
the code above, however I tell ya, this is the best I can get so far and I
figure I should share it with you. Its the least I can do. I don't say that
ist the best way of doing this, however, it works!
I will try to look for more documentation so I can get familliar with the
whole library options. I don't know if there is other ways of reading an
rs232 port asides from using the SerialWnd class.... I see people using Read
and write functions????? Anyhow for now this will do
But I will be honest with you Heinz, in spite of all the help I have gotten,
at a certain point, I was reading over and over all the *much appreciated*
posts from everyone, and then I read over the rs232 documentation, and then I
re-read your post.... and your last paragraph is what gave it away:
"Read the documentation of CSerial what EReadTimeoutBlocking really means.
The name suggests that the read hangs until the requested number of bytes is
available. Such behaviour does not really work well with (Windows-) message
driven applications. When you read whenever data is signaled to your
application, you should use small timeouts and never read more bytes then
available at that time."
This paragraph somehow lead me back to the documentation and thats where I
read about the following snippet of code in the document.
==================================================
DWORD dwBytesRead = 0;
BYTE abBuffer[100];
do
{
// Read data from the COM-port
serial.Read(abBuffer,sizeof(abBuffer),&dwBytesRead);
if (dwBytesRead > 0)
{
// TODO: Process the data
}
}
while (dwBytesRead == sizeof(abBuffer));
==============================================
I !!!!only!!!! read the document 3 times (How slow can I be!), And the
explanation stipulates:
"Make sure you always read the entire buffer after receiving the EEventRecv
event to avoid you lose data."
And so I adapted it to what I did and merging the combined efforts of all
the posts, I got something that is functional.
In short, for now, I will use what works!
Thankyou very much Heinz, John, Scott and Frank ! All posts are much
appreciated!
--
Best regards
Robert
"Heinz Ozwirk" wrote:
"Robby" <Robby@discussions.microsoft.com> schrieb im Newsbeitrag
news:91E88AF3-C15C-4E6E-9D57-C5E79F7F688D@microsoft.com...
Hello,
At this point if anyone has used the CSerialWnd class from code project, I
am desperately in need of a code sample doing a Read task from rs232.
I have been exploiting this subject with some of the most credible fellows
on this newsgroups and I fully appreciated their support and wish to
continue. I am re-starting this subject so not to clutter the previous
post
and to give the full story in case I didn't post it right before.
Right now I have the following code:
===========================================
//Global object declared one time
CSerialWnd MySerial;
LRESULT CALLBACK WndProc_CW1 (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static DWORD dwBytesRead = 23;
static BYTE abBuffer[23];
static TCHAR szBuffer[49];
MySerial.Open(TEXT("COM1"),hwnd,WM_NULL,lParam,0,0);
//Serial port monitor
if (message == CSerialWnd::mg_nDefaultComMsg)
{
// A serial message occurred
const CSerialWnd::EEvent eEvent = CSerialWnd::EEvent(LOWORD(wParam));
const CSerialWnd::EError eError = CSerialWnd::EError(HIWORD(wParam));
MySerial.Setup(CSerial::EBaud9600,CSerial::EData8,
CSerial::EParNone,CSerial::EStop1);
MySerial.SetupReadTimeouts(CSerial::EReadTimeoutBlocking);
switch (eEvent)
{
case CSerialWnd::EEventRecv:
MySerial.Read(abBuffer,sizeof(abBuffer),&dwBytesRead);
//abBuffer[dwBytesRead] = 0; //Null character is now included in
receiving msg
break;
default:
break;
}
MultiByteToWideChar(CP_ACP,0, (LPCSTR)abBuffer,23,szBuffer,49);
MySerial.Close();
return 0;
}
I don't know the details of your CSerialWnd class, so I can only add some
general comments.
1. Usually you should not open the port, initialize it and close it again in
each and every message sent to a window. Usually you should open and
initialize the port once when your program starts and close it only when
your program terminates (or you don't want to receive more data).
2. Often serial ports don't give you the number of bytes you are asking for.
You'll get as many bytes as are available when the read function is called.
You have to test the number of bytes returned, and if not enough data is
available yet, you have to store what you have got and wait for more data to
be sent.
3. Often serial ports signal each byte they are receiving, or or groups of
bytes when there is some delay between them. But the receiver might read all
bytes when it processes the first event. Then more events may be in the
window's message queue, but the bytes causing them have already been read.
This especially happens when you slow down your receiver while debugging.
For the first byte a message is sent to your window. This message is
processed and execution stops at the breakpoint before calling Read. While
the debugger waits for you to step through your code, mode data might arrive
and Read will return those bytes, too. But messages generated for those
bytes are still in the queue and will be processed once you have stepped
through your code. Now when the next message is processed, the data causing
that message have already been read, and Read will wait for the specified
time. Try to find out how to determine the number of bytes available and
only read that many bytes.
4. Don't use too large timeouts. The read intervall timeout depends on the
behaviour of the sender of your data. How long does it need between sending
two bytes. This timeout should be large enough for the sender to fetch and
transmit the next byte. But values about 100ms should be large enough. The
timeout multiplier depends on the selected baud rate. The higher that rate,
the smaller this value can be. With 9600 baud you can transmit a little less
than 1000 characters per second, so this timeout could be 2ms, but larger
values usualy will not be harmfull. The total constant timeout basically
determines the time a blocking read waits when no data is available. Usually
I use values between 0.5 and 5 seconds.
But whatever timeouts you are using, be prepared that you do not read as
many bytes as you are expecting. Provide some way to collect data of
multiple reads and process them only when you got all the data you need.
The correct message was returned. However, immediately after, VC++ shows
my
program going back again to the break point a second time without me
having
sent another message to the port. I guess this event is caused by some
junk
left over in the serial buffer. So I step to the Read statement.... and
then
VC++ hangs thereafter!
First, I don't know if my Comm timeout is well set.... ? Obviously, if it
were, well then the Read function would of returned.... right?
Read the documentation of CSerial what EReadTimeoutBlocking really means.
The name suggests that the read hangs until the requested number of bytes is
available. Such behaviour does not really work well with (Windows-) message
driven applications. When you read whenever data is signaled to your
application, you should use small timeouts and never read more bytes then
available at that time.
HTH
heinz