serial communication between PC and Arduino unpredictable

Hello guys,

Recently I'm trying to send binary data to arduino with C++, and find that sometimes it works, and sometimes it doesn't. And when I run the exe multiple times, there is a pattern: working ---> not working ---> working .....

I guess this has something to do the buffer, but after trying different methods, I'm still confused by this weird behavior. So I decided to post the code and seek help from you. Thanks!

Enviroment: MingW, Arduino Uno R3
C++ code:
Serial.cpp

#include "Serial.h"
Serial::Serial(void)
{

}
void Serial::ConnectSerial(LPCSTR portName)
{
	//We're not yet connected
	this->connected = false;

	//Try to connect to the given port throuh CreateFile
	this->hSerial = CreateFile(portName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	//Check if the connection was successfull
	if(this->hSerial==INVALID_HANDLE_VALUE)
	{
		//If not success full display an Error
		if(GetLastError()==ERROR_FILE_NOT_FOUND){

			//Print Error if neccessary
			printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);

		}
		else
		{
			printf("ERROR!!!");
		}
	}
	else
	{
		//If connected we try to set the comm parameters
		DCB dcbSerialParams = {0};

		//Try to get the current
		if (!GetCommState(this->hSerial, &dcbSerialParams))
		{
			//If impossible, show an error
			printf("failed to get current serial parameters!");
		}
		else
		{
			//Define serial connection parameters for the arduino board
			dcbSerialParams.BaudRate=CBR_115200;
			dcbSerialParams.ByteSize=8;
			dcbSerialParams.StopBits=ONESTOPBIT;
			dcbSerialParams.Parity=NOPARITY;

			//Set the parameters and check for their proper application
			if(!SetCommState(hSerial, &dcbSerialParams))
			{
				printf("ALERT: Could not set Serial Port parameters");
			}
			else
			{
				//If everything went fine we're connected
				this->connected = true;
				//We wait 2s as the arduino board will be reseting
				Sleep(ARDUINO_WAIT_TIME);
			}
		}
	}
	flush();

}

Serial::~Serial()
{
	//Check if we are connected before trying to disconnect
	if(this->connected)
	{
		//We're no longer connected
		this->connected = false;
		//Close the serial handler
		CloseHandle(this->hSerial);
	}
}

int Serial::ReadData(char *buffer, unsigned int nbChar)
{
	//Number of bytes we'll have read
	DWORD bytesRead;
	//Number of bytes we'll really ask to read
	unsigned int toRead;

	//Use the ClearCommError function to get status info on the Serial port
	ClearCommError(this->hSerial, &this->errors, &this->status);

	//Check if there is something to read
	if(this->status.cbInQue>0)
	{
		//If there is we check if there is enough data to read the required number
		//of characters, if not we'll read only the available characters to prevent
		//locking of the application.
		if(this->status.cbInQue>nbChar)
		{
			toRead = nbChar;
		}
		else
		{
			toRead = this->status.cbInQue;
		}

		//Try to read the require number of chars, and return the number of read bytes on success
		if(ReadFile(this->hSerial, buffer, toRead, &bytesRead, NULL) && bytesRead != 0)
		{
			return bytesRead;
		}

	}

	//If nothing has been read, or that an error was detected return -1
	return -1;

}


bool Serial::WriteData(char *buffer, unsigned int nbChar)
{
	DWORD bytesSend;

	//Try to write the buffer on the Serial port
	if(!WriteFile(this->hSerial, (void *)buffer, nbChar, &bytesSend, 0))
	{
		//In case it don't work get comm error and return false
		ClearCommError(this->hSerial, &this->errors, &this->status);

		return false;
	}
	else
		return true;
}

bool Serial::IsConnected()
{
	//Simply return the connection status
	return this->connected;
}
void Serial::WriteIntData(unsigned int val)
{
	union serial_super_data {
		unsigned int int_4_bytes;
		unsigned char send_byte[4];}
	super_data;
	super_data.int_4_bytes = val;
	for (int i=0; i!=4; ++i){
		WriteData((char *)(&(super_data.send_byte[i])), 1);
	}
}

void Serial::WriteIntDataArray(int *val, int sz) 
{
	for (int i=0; i!=sz; ++i) {
		WriteIntData(val[i]);
	}

}
void Serial::flush()
{
	FlushFileBuffers(hSerial);
}

Serial.h

#ifndef SERIAL_H
#define SERIAL_H
#define ARDUINO_WAIT_TIME 2000

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

class Serial
{
private:
	//Serial comm handler
	HANDLE hSerial;
	//Connection status
	bool connected;
	//Get various information about the connection
	COMSTAT status;
	//Keep track of last error
	DWORD errors;

public:
	//Initialize Serial communication with the given COM port
	Serial(void);
	void ConnectSerial(LPCSTR portName);
	//Close the connection
	//NOTA: for some reason you can't connect again before exiting
	//the program and running it again
	~Serial();
	//Read data in a buffer, if nbChar is greater than the
	//maximum number of bytes available, it will return only the
	//bytes available. The function return -1 when nothing could
	//be read, the number of bytes actually read.
	int ReadData(char *buffer, unsigned int nbChar);
	//Writes data from a buffer through the Serial connection
	//return true on success.
	bool WriteData(char *buffer, unsigned int nbChar);
	void WriteIntData(unsigned int val);
	void WriteIntDataArray(int *val, int sz);
	//Check if we are actually connected
	bool IsConnected();
	void flush(void);

};

#endif // SERIALCLASS_H_INCLUDED

testlibserial.cpp

#include <windows.h>
#include <ctime>
#include "Serial.h"
#include <iostream>
using namespace std;

Serial myserial;
int main(int argc, char* argv[])
{
	myserial.ConnectSerial("com5");
	Sleep(2);
	std::cout << "ready for output" << std::endl;
	char dd = getchar();

	// ========================
	for (int j=0; j!=8; j++) {
	  myserial.WriteIntData(10);
	  myserial.flush();
	  Sleep(1000);
	}
	return 0;
}

Arduino side:

#include <math.h>
int ledPin = 13;//pin 13 has a LED connected on most arduino boards
unsigned long duration = 0;

/* ====================== */
void setup()
{
	pinMode(ledPin, OUTPUT);
	/* for (int i=2; i <= 11; i++) { */
	/* 	pinMode(i, OUTPUT); */
	/* } */
	Serial.begin(115200);
	/* while (Serial.available()) */
	/*   Serial.read(); */
}

void loop()
{

  while (Serial.available() >= 4) {
    receiveBinary(&duration);
    if (duration==10)
      {
	digitalWrite(ledPin, HIGH);
	delay(100);
	digitalWrite(ledPin, LOW);
      }
    else {
      digitalWrite(ledPin, HIGH);
      delay(1000);
      digitalWrite(ledPin, LOW);
    }
  }
  
}
void receiveBinary(unsigned long *val)
{
	union {
		unsigned char b[4];
		unsigned long longdata;
	} foo;
	foo.b[0] = Serial.read();
	foo.b[1] = Serial.read();
	foo.b[2] = Serial.read();
	foo.b[3] = Serial.read();
	*val = foo.longdata;
}

I ran the code multiple times with the same result, so I guess that means it works. The problem is probably in the DCB structure. Explicitly, set all of the fields in the structure. Also, remember you should prefix the "com port" name with "\\\\.\\" (ex. "\\\\.\\COM5"). Can you say what's not working that would help? Listed below is a code snippet from a program of mine.

   DCB dcb;
   dcb.DCBlength = sizeof(DCB);
   if (!GetCommState(h,&dcb))
   {
      return 1;
   }

   dcb.BaudRate      = CBR_115200;
   dcb.fBinary       = TRUE;
   dcb.fParity       = FALSE;
   dcb.fOutxCtsFlow  = FALSE;
   dcb.fOutxDsrFlow  = FALSE;
   dcb.fDtrControl   = DTR_CONTROL_DISABLE;
   dcb.fDsrSensitivity = FALSE; // TODO: should this be TRUE
   dcb.fNull         = FALSE;
   dcb.fOutX         = FALSE;
   dcb.fInX          = FALSE;
   dcb.fRtsControl   = RTS_CONTROL_DISABLE;
   dcb.fAbortOnError = TRUE;
   dcb.ByteSize      = 8;
   dcb.Parity        = NOPARITY;
   dcb.StopBits      = ONESTOPBIT;

   if (!SetCommState(h, &dcb))
   {
      return 1;
   }

   COMMTIMEOUTS times;
   times.ReadIntervalTimeout         = MAXDWORD;
   times.ReadTotalTimeoutMultiplier  = MAXDWORD;
   times.ReadTotalTimeoutConstant    = 15;
   times.WriteTotalTimeoutMultiplier = 0;
   times.WriteTotalTimeoutConstant   = 0;

   // I want ReadFile to return immediately when 1 or more bytes are in the queue
   if (!SetCommTimeouts(h, &times))
   {
      return 1;
   }

Thanks for the reply and sorry for my unclear description!

The symptom is that:
When I run the code for the first time, which send the number 10 as long to arduino multiple times, the arduino receive that number and would find it's not 10, so will flash the led pin for 1 second multiple times
When I do this for the second time, it will find the number is 10, and will blink for 100 ms multiple times.

In fact I don't quite understand the structure of DCB, the Serial class is from someone else. However I changed the relevant part of the code to what you've suggested, the problem remains.

If you compiled the code, but can't repeat my problem, then I have no idea what is causing this weird thing.

Try running PortMon to see what is being send to and from the serial port.

Thanks a lot for this information! Looks appealing.