What I am trying to do is to get frames of data from a COM port. The data frames are 258 bytes each, and are constantly coming in at a rate of about 8.5 per second.
I need to receive the data frames and then I will do some other processing on them, but for now I am trying to just print them out to the console. Each data frame starts with '/'
and ends with '?'
.
Here is the code I wrote (yes I'm a noob at this):
#include <iostream>
#include <time.h>
#include <string>
#include "windows.h"
#pragma comment (lib, "OneCore.lib")
int main()
{
int portnum = 0;
ULONG ptNums[300];
ULONG some = 300;
ULONG ptsFound[20];
LPCSTR fName = "COM4";
ULONG status = GetCommPorts(ptNums, 100, ptsFound);
if (status == ERROR_SUCCESS) {
std::cout << "ports found:" << ptsFound[0] << "\n";
for (int i = 0; i < ptsFound[0]; i++) {
portnum = ptNums[i];
std::cout << "COM" << portnum << "\n";
}
}
HANDLE port = CreateFileA(fName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (port == INVALID_HANDLE_VALUE) {
std::cout << "unable to open port\n";
}
DCB dcb;
dcb = { 0 };
//this is the default, use 8N1
dcb.DCBlength = sizeof(DCB);
dcb.fBinary = TRUE;
dcb.BaudRate = 250000;
dcb.fParity = FALSE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
DWORD eventMask;
SetCommState(port, &dcb);
SetCommMask(port,EV_TXEMPTY|EV_RXCHAR);
bool exitloop = false;
char arduBuf[258];
OVERLAPPED ov;
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = CreateEvent(0, TRUE, 0, 0);
SetupComm(port, 300, 300);
while (!exitloop) {
GetCommMask(port, &eventMask);
BOOL dataRet = WaitCommEvent(port, &eventMask, &ov);
DWORD dwait = WaitForSingleObject(ov.hEvent, INFINITE);
if (eventMask & EV_TXEMPTY) {
std::cout << "all data was sent\n";
exitloop = true;
}
if (dwait == WAIT_OBJECT_0) {
GetCommMask(port, &eventMask);
std::cout << "some random stuff was received\n";
BOOL status = ReadFile(port, arduBuf, sizeof(arduBuf), NULL, &ov);
std::cout << "status:" << status << "\nerror is:" << GetLastError() << "\n";
PurgeComm(port, PURGE_RXCLEAR);
if (status) {
std::cout << arduBuf << "\n";
}
ResetEvent(ov.hEvent);
//exitloop = true;
}
if (dwait == WAIT_TIMEOUT) {
std::cout << "timeout\n";
}
}
}
The code compiles and runs, and WaitForSingleObject()
returns WAIT_OBJECT_0
, but the status is always 0 and GetLastError()
returns ERROR_IO_PENDING
. If I print out the data buffer regardless, then I can see my data, but its on a few jumbled lines and very inconsistent:
I think this is because although EV_RXCHAR
is satisfied, EV_TXEMPTY
never is. If I set the mask to EV_TXEMPTY
only, then the code hangs at WaitForSingleObject()
. I think this is because before the data gets read, there is new data already filling the input buffer.
My questions:
- There is obviously something wrong here. Can someone point it out?
- I am not sure if stuffing the reading into a
while
loop is a good idea. I know I should stuff it into a thread that terminates once a read has failed or was successful, but is thewhile
loop actually causing problems? - I get a warning on the
WaitForSingleObject()
line thatov.hEvent
could be 0. Why is this so, and can it also be an issue? - When do I need to reset the event?
As you can tell, I am new to COM port I/O. I've spent over a day on this and decided it was time to ask someone that knows what they are doing.
As @tevemadar said,
ERROR_IO_PENDING
is not a failure; it designates the read operation is pending completion asynchronously.If the overlapped operation cannot be completed immediately, the function returns FALSE and the
GetLastError
function returnsERROR_IO_PENDING
, indicating that the operation is executing in the background. When this happens, the system sets the hEvent member of theOVERLAPPED
structure to the not-signaled state beforeWaitCommEvent
returns, and then it sets it to the signaled state when one of the specified events or an error occurs.Using the
GetOverlappedResult
function to determine the results of theWaitCommEvent
operation can get some updates.