I've been trying to establish a communication with an Arduino Nano (a cheap Chinese copy actually).
The board sends 3 characters every seconds, I only get the last 2 characters
I found this documentation that refers to the MTTY sample. I went ahead and found it to take it as an exemple.
I'm intersted in understanding how the reading part is done (in READSTAT.C)
This part confuses me :
while ( !fThreadDone ) {
//
// If no reading is allowed, then set flag to
// make it look like a read is already outstanding.
//
if (NOREADING( TTYInfo ))
fWaitingOnRead = TRUE;
//
// if no read is outstanding, then issue another one
//
if (!fWaitingOnRead) {
if (!ReadFile(COMDEV(TTYInfo), lpBuf, AMOUNT_TO_READ, &dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
ErrorInComm("ReadFile in ReaderAndStatusProc");
fWaitingOnRead = TRUE;
}
else { // read completed immediately
if ((dwRead != MAX_READ_BUFFER) && SHOWTIMEOUTS(TTYInfo))
UpdateStatus("Read timed out immediately.\r\n");
if (dwRead)
OutputABuffer(hTTY, lpBuf, dwRead);
}
}
//
// If status flags have changed, then reset comm mask.
// This will cause a pending WaitCommEvent to complete
// and the resultant event flag will be NULL.
//
if (dwStoredFlags != EVENTFLAGS(TTYInfo)) {
dwStoredFlags = EVENTFLAGS(TTYInfo);
if (!SetCommMask(COMDEV(TTYInfo), dwStoredFlags))
ErrorReporter("SetCommMask");
}
//
// If event checks are not allowed, then make it look
// like an event check operation is outstanding
//
if (NOEVENTS(TTYInfo))
fWaitingOnStat = TRUE;
//
// if no status check is outstanding, then issue another one
//
if (!fWaitingOnStat) {
if (NOEVENTS(TTYInfo))
fWaitingOnStat = TRUE;
else {
if (!WaitCommEvent(COMDEV(TTYInfo), &dwCommEvent, &osStatus)) {
if (GetLastError() != ERROR_IO_PENDING) // Wait not delayed?
ErrorReporter("WaitCommEvent");
else
fWaitingOnStat = TRUE;
}
else
// WaitCommEvent returned immediately
ReportStatusEvent(dwCommEvent);
}
}
//
// wait for pending operations to complete
//
if ( fWaitingOnStat && fWaitingOnRead ) {
dwRes = WaitForMultipleObjects(NUM_READSTAT_HANDLES, hArray, FALSE, STATUS_CHECK_TIMEOUT);
switch(dwRes)
{
//
// read completed
//
case WAIT_OBJECT_0:
if (!GetOverlappedResult(COMDEV(TTYInfo), &osReader, &dwRead, FALSE)) {
if (GetLastError() == ERROR_OPERATION_ABORTED)
UpdateStatus("Read aborted\r\n");
else
ErrorInComm("GetOverlappedResult (in Reader)");
}
else { // read completed successfully
if ((dwRead != MAX_READ_BUFFER) && SHOWTIMEOUTS(TTYInfo))
UpdateStatus("Read timed out overlapped.\r\n");
if (dwRead)
OutputABuffer(hTTY, lpBuf, dwRead);
}
fWaitingOnRead = FALSE;
break;
//
// status completed
//
case WAIT_OBJECT_0 + 1:
if (!GetOverlappedResult(COMDEV(TTYInfo), &osStatus, &dwOvRes, FALSE)) {
if (GetLastError() == ERROR_OPERATION_ABORTED)
UpdateStatus("WaitCommEvent aborted\r\n");
else
ErrorInComm("GetOverlappedResult (in Reader)");
}
else // status check completed successfully
ReportStatusEvent(dwCommEvent);
fWaitingOnStat = FALSE;
break;
//
// status message event
//
case WAIT_OBJECT_0 + 2:
StatusMessage();
break;
//
// thread exit event
//
case WAIT_OBJECT_0 + 3:
fThreadDone = TRUE;
break;
case WAIT_TIMEOUT:
//
// timeouts are not reported because they happen too often
// OutputDebugString("Timeout in Reader & Status checking\n\r");
//
//
// if status checks are not allowed, then don't issue the
// modem status check nor the com stat check
//
if (!NOSTATUS(TTYInfo)) {
CheckModemStatus(FALSE); // take this opportunity to do
CheckComStat(FALSE); // a modem status check and
// a comm status check
}
break;
default:
ErrorReporter("WaitForMultipleObjects(Reader & Status handles)");
break;
}
}
}
I think this code is likely to call ReadFile
without calling WaitCommEvent
, is it correct to proceed this way ? Also I always tried to :
WaitCommEvent
ReadFile
WaitForSingleObject
GetOverlappedResult
ReadFile
to actually get the datas but it doesn't seem to be necessaryI tryied using this exemple without call WaitCommEvent
and it works a little bit, the read operations always miss the first character.
I open the port with GENERIC_WRITE
, GENERIC_READ
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
and I appropriately call SetCommMask
with the EV_RXCHAR
flag.
DWORD WINAPI read(LPVOID param) {
CUSTOM_DATA* params = (CUSTOM_DATA*)param;
HANDLE hComm = params->hComm;
OVERLAPPED ov = { 0 };
bool waitingOnStat = FALSE;
int i = 1;
while (1) {
char* buffer = (char*)malloc(100 * sizeof(char));
ZeroMemory(buffer, 100);
DWORD bufferSize = 100;
DWORD nbRead;
DWORD evtMask;
DWORD res;
if (!waitingOnStat) {
ZeroMemory(&ov, sizeof(OVERLAPPED));
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ReadFile(hComm, buffer, bufferSize, &nbRead, &ov)) {
if (GetLastError() != ERROR_IO_PENDING) {
printf("ERROR_IO NOT PENDING\n");
}
waitingOnStat = true;
}
else {
std::cout.write(buffer, nbRead) << std::endl;
waitingOnStat = FALSE;
}
}
if (waitingOnStat) {
printf("Waiting");
DWORD res = WaitForSingleObject(ov.hEvent, INFINITE);
switch (res) {
case WAIT_OBJECT_0:
waitingOnStat = FALSE;
DWORD nbBytesTransfered;
if (!GetOverlappedResult(hComm, &ov, &nbBytesTransfered, TRUE)) {
printf("error reading %d\n", GetLastError());
} else {
std::cout.write(buffer, nbRead);
}
break;
default:
printf("waiting read error %d\n", GetLastError());
break;
}
}
i++;
}
}
Why is ReadFile
constantly returning true ?
Do I need to call WaitCommEvent
With @Ian Abbott's help I fiddled with the parameters for COMMTIMEOUTS
.
The documentation specifies some behavior such as :
If an application sets
ReadIntervalTimeout
andReadTotalTimeoutMultiplier
toMAXDWORD
and setsReadTotalTimeoutConstant
to a value greater than zero and less thanMAXDWORD
, one of the following occurs when theReadFile
function is called:
- If there are any bytes in the input buffer,
ReadFile
returns immediately with the bytes in the buffer.- If there are no bytes in the input buffer,
ReadFile
waits until a byte arrives and then returns immediately.- If no bytes arrive within the time specified by
ReadTotalTimeoutConstant
,ReadFile
times out.
The thing is with my code I was :
GetOverlappedResult
and then ignoring the nbBytesRead
param (because I had read that it should be ignored when using overlapped I/O).Taking the following measures seem to have corrected my issue :
Use nbBytesTransfered
in the when after calling GetOverlappedResult
. Although if I set the timeouts to :
timeouts.ReadIntervalTimeout = 4294967295;
timeouts.ReadTotalTimeoutConstant = 4294967294;
timeouts.ReadTotalTimeoutMultiplier = 4294967295;
ReadFile
will return FALSE
for the first character that will end up being displayed by the GetOverlappedResult
call, then ReadFile
will return TRUE
and will display the remaining characters (I haven't tested this at high speed).
If I set the timeouts as follow :
timeouts.ReadIntervalTimeout = 10;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.ReadTotalTimeoutMultiplier = 10;
ReadFile
doesn't return TRUE
and GetOverlappedResult
reads all of the characters (I haven't tested this at high speed).