vMicro (Serial) gets correct COM PORT Data, C++ App doesn't

Im following this video were it is really good explained how to write an class to read serial communication. I use visualMicro to write code on to the arduino. In the serial window it is working good, the expectetd output is shown.


void setup()
{
    Serial.begin(115200);
}



void loop()
{
    if (Serial.availableForWrite())
    {
        Serial.write("60.00\r\n");
     
    }
    delay(500);
}

However, when i use the C++ class i get an incorrect output:
image
Can anyone see a mistake in the code?

Header for class:

#pragma once

#define INPUT_DATA_BYTES 7
#define ARDUINO_WAIT_TIME 1500

/*#define SERIAL_START_MESSAGE "<"
#define SERIAL_STOP_MESSAGE ">"*/

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

#include <iostream>

class SerialPort
{
public:
	SerialPort(char* COM_PORT);
	~SerialPort();

	int ReadSerialPort(char* Buffer, unsigned int BufferSize);
	bool IsConnected();

private:
	HANDLE	SerialPortHandler;
	bool	ComPortConnected;
	COMSTAT status;
	DWORD	errors;
	DWORD	errorMsg;
	
};

SourceCode for class

#include "SerialPort.h"

SerialPort::SerialPort(char* COM_PORT)
{
	errors = NULL;
	status = { 0 };
	ComPortConnected = false;



	SerialPortHandler = CreateFileA(static_cast<LPCSTR>(COM_PORT), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	
	errorMsg = GetLastError();

	if (errorMsg == 2)
	{
		std::cout << "ERROR: COM PORT NOT FOUND!" << std::endl;
	}

	if (errorMsg == 5)
	{
		std::cout << "ERROR: COM PORT ACCES DENIED!" << std::endl;
	}

	//If Port found procced with connection
	else if(errorMsg == 0)
	{
		DCB SerialPortSettings = { 0 };
		if (!GetCommState(SerialPortHandler, &SerialPortSettings))
		{
			std::cout << "ERROR: FAILED TO GET CURRENT SERIAL PARAMETERS!" << std::endl;
		}

		//Set ComPort Settings
		else
		{
			SerialPortSettings.BaudRate = CBR_115200;
			SerialPortSettings.fDtrControl = DTR_CONTROL_DISABLE;
			SerialPortSettings.ByteSize = 8;
			SerialPortSettings.Parity = NOPARITY;
			SerialPortSettings.StopBits = ONESTOPBIT;

			//Check for failed COM Port settings
			if (!SetCommState(SerialPortHandler, &SerialPortSettings))
			{
				std::cout << "ERROR: FAILED TO SET COM PORT PARAMETERS" << std::endl;
			}
			else
			{
				ComPortConnected = true;
				PurgeComm(SerialPortHandler, PURGE_RXCLEAR | PURGE_TXCLEAR);
				Sleep(ARDUINO_WAIT_TIME);
			}
		}
	}
}

SerialPort::~SerialPort()
{
	if (ComPortConnected)
	{
		ComPortConnected = false;
		CloseHandle(SerialPortHandler);
		std::cout << "COM PORT CLOSED" << std::endl;
	}
}

int SerialPort::ReadSerialPort(char* Buffer, unsigned int BufferSize)
{
	DWORD BytesRead;
	unsigned int BytesToRead = 0;
	

	ClearCommError(SerialPortHandler, &errors, &status);

	if (status.cbInQue > 0)
	{
		if (status.cbInQue > BufferSize)
		{
			BytesToRead = BufferSize;
		}
		else
		{
			BytesToRead = status.cbInQue;
		}
	}

	if (ReadFile(SerialPortHandler, Buffer, BytesToRead, &BytesRead, NULL))
	{

		return BytesRead;
	}

	return 0;
}

bool SerialPort::IsConnected()
{
	if (ComPortConnected)
	{
		return true;
	}
	else
	{
		return false;
	}
}

Main:

#include <iostream>
#include <iomanip>
#include <string>
#include <Windows.h>
#include <thread>

#include "SerialPort.h"

int main()
{
	char InputData[INPUT_DATA_BYTES];
	double InputValueDouble = 0.0;
	
	char Port[] = "COM5";
	char* COM_PORT = Port;

	SerialPort ArduinoPort(COM_PORT);


	if (ArduinoPort.IsConnected())
	{
		std::cout << "CONNECTED TO ARDUINO ON " << Port << std::endl;
	}

	while (ArduinoPort.IsConnected())
	{
		ArduinoPort.ReadSerialPort(InputData, INPUT_DATA_BYTES);
		 
		std::string InputDataString(InputData);

		std::cout << InputDataString << '\n';

		

		std::this_thread::sleep_for(std::chrono::milliseconds(500));
	}
	
	return 0;
}

I already tried with different board (UNO/MEGA), different Baudrates (9600/115200), checked the device manager(Baudrate was correct to....

Any help is appreciated

I’m not a windows guy but you might Need a trailing null char added to the byte buffer before converting it to a String and also check if the call to ReadSerialPort() returned 0

thank you very much for your response...based on your suggestion i changed the "ReadSerialPort" function a bit so i can check in the console by every run the returnvalue from ReadFile(which is always 1 so good) and tried different cout variants (as char array in one run and in a for loop every element in one line-->should not make a difference anyway) but still no success. Interestingly, by every run of the "ReadSerialPort" function, the output is a little bit different, sometimes it is indeed "60.00" and sometimes it writes something completly different in the char array.... now i work on the null char, but as im quite new to the language, this is gonna need some time
thank you and have a good day :smile:

well a a starter I would not try to create something to print if you did not read anything so check for 0 length

	while (ArduinoPort.IsConnected()) {
		if (ArduinoPort.ReadSerialPort(InputData, INPUT_DATA_BYTES) != 0) { 
		    std::string InputDataString(InputData);
		    std::cout << InputDataString << '\n';
		    std::this_thread::sleep_for(std::chrono::milliseconds(500));
	    }
	}

but this is assuming that what you get back from ReadSerialPort() in InputData can be transformed correctly into a string, which I don't think is guaranteed as it's just a byte buffer with no end marker.

In C or C++ when a buffer represents a string you add a trailing null char to let the parsing functions know where to stop reading in memory so if the read is successful then you should add a trailing null in the buffer

You could change that in the ReadSerialPort() function with a bit of a hack probably but this is not great as you "pollute" the buffer with a trailing null so best is to handle the fact that you know how many legit bytes made it into the buffer and deal with it in the calling part of the code, may be something like this then

	while (ArduinoPort.IsConnected()) {
		int count = ArduinoPort.ReadSerialPort(InputData, INPUT_DATA_BYTES);
		if (count != 0) { 
		    std::string InputDataString(InputData, count); // only use the count first bytes of the buffer
		    std::cout << InputDataString << '\n';
		    std::this_thread::sleep_for(std::chrono::milliseconds(500));
	    }
	}

this is using the std::string constructor that takes a buffer and a length

from buffer (5) string (const char* s, size_t n);

1 Like

Thanks again for your help. After adding your suggestion

Blockzitat

to the code, it showed the string as it should in the console, that means no "empty" characters at the end.
However, the content of the string is still in some way random, sometimes it is "60.00" and sometimes it is something completely different. I quite sure that the variable "InputData" doesn't even get (ReadFile doesn't read) the correct data in correct order from the COM PORT Buffer in the first place, i'm sure somewhere on the way I made a dumb mistake or forgot something and don't see it now.

But again, thank you very much for your time and help, I already learned a lot more

probably need to read more on how to deal with a Serial port on a PC then... I'm not an expert there

Finally with your help and inspiration from these great guys and their project/OpenSourceCode i changed my approach a little and addet front&end delimiters to the string from the arduino code which i check in my C++ Code:

ReadSerialPort:

std::string SerialPort::ReadSerialPort()
{
	

	DWORD BytesRead;
	char DataCharacter[1];
	bool ReadData = false;
	std::string StringMessage;
	bool ReadFileSuccess = false;

	ClearCommError(SerialPortHandler, &errors, &status);

	while (status.cbInQue > 0)
	{
		ReadFileSuccess = ReadFile(SerialPortHandler, DataCharacter, 1, &BytesRead, NULL);
		if (DataCharacter[0] == '<' || ReadData == true)
		{
			ReadData = true;

			if (DataCharacter[0] != '<' && DataCharacter[0] != '>' && ReadData == true)
			{
				StringMessage.append(DataCharacter, 1);
			}

			if (DataCharacter[0] == '>')
			{
				ReadData = false;
				return StringMessage;
			}
		}

		else
		{
			//std::cout << "ERROR: FAILED TO FIND FRONT DELIMITER '<' " << std::endl;
		}
		

	}	
	
	if (!ReadFileSuccess)
	{
		return "Failed try";
	}
	
}

main.cpp to write string in console:

while (ArduinoPort.IsConnected())
	{
		
		std::string Message = ArduinoPort.ReadSerialPort();
		
		if (Message != "Failed try")
		{
			std::cout << Message << std::endl;



			std::cout << '\n';
			std::this_thread::sleep_for(std::chrono::milliseconds(500));
		}
		
				
	}

Now it works quite good, i'm just not quite sure if it is really cleaning the COM PORT Buffer as the ReadFile()function does(atleast as i understand it) but i guess i'm gonna cross that bridge when i get there :smile:

Thank you for helping me and have a nice day

great - happy end of the year

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.