[C + + open source library] easy to use serial communication API library under Windows and linux

previously on

I wrote two blogs about serial port before

The first part mainly introduces the use methods and software recommendations of virtual serial port simulator and serial port debugging assistant.

The second part mainly uses the above two software and C language program to periodically receive the data sent by the virtual serial port. The previous C language code is OK for testing, but if serial port communication is required in the actual project, we'd better install the serial port communication package into a reusable module, Next, I will recommend an easy-to-use serial communication module in C + + language, and give an example combined with virtual serial port simulator and serial port debugging assistant.

github address: https://github.com/ayowin/WzSerialPort

Reference blog: Realization of serial communication in windows pure C + +,Using C + + to realize serial communication under windows,[C + +] Windows API serial communication general class source code

First, the code files of the open source project are given. There are only three files. There are not many codes. All of them are posted below:

WzSerialPort.cpp

#include "WzSerialPort.h"

#include <stdio.h>
#include <string.h>

#include <WinSock2.h>
#include <windows.h>

WzSerialPort::WzSerialPort()
{

}

WzSerialPort::~WzSerialPort()
{

}

bool WzSerialPort::open(const char* portname,
						int baudrate,
						char parity,
						char databit,
						char stopbit,
						char synchronizeflag)
{
	this->synchronizeflag = synchronizeflag;
	HANDLE hCom = NULL;
	if (this->synchronizeflag)
	{
		//Synchronization mode
		hCom = CreateFileA(portname, //Serial port name
									GENERIC_READ | GENERIC_WRITE, //Support reading and writing
									0, //Exclusive mode, serial port does not support sharing
									NULL,//Security property pointer, the default value is NULL
									OPEN_EXISTING, //Open the existing serial port file
									0, //0: synchronization mode, FILE_FLAG_OVERLAPPED: asynchronous mode
									NULL);//Used to copy file handles. The default value is NULL. For serial ports, this parameter must be set to NULL
	}
	else
	{
		//Asynchronous mode
		hCom = CreateFileA(portname, //Serial port name
									GENERIC_READ | GENERIC_WRITE, //Support reading and writing
									0, //Exclusive mode, serial port does not support sharing
									NULL,//Security property pointer, the default value is NULL
									OPEN_EXISTING, //Open the existing serial port file
									FILE_FLAG_OVERLAPPED, //0: synchronization mode, FILE_FLAG_OVERLAPPED: asynchronous mode
									NULL);//Used to copy file handles. The default value is NULL. For serial ports, this parameter must be set to NULL
	}
	
	if(hCom == (HANDLE)-1)
	{		
		return false;
	}

	//Configure buffer size 
	if(! SetupComm(hCom,1024, 1024))
	{
		return false;
	}

	// configuration parameter 
	DCB p;
	memset(&p, 0, sizeof(p));
	p.DCBlength = sizeof(p);
	p.BaudRate = baudrate; // Baud rate
	p.ByteSize = databit; // Data bit

	switch (parity) //Check bit
	{   
	case 0:   
		p.Parity = NOPARITY; //No check
		break;  
	case 1:   
		p.Parity = ODDPARITY; //Odd check
		break;  
	case 2:
		p.Parity = EVENPARITY; //Parity check
		break;
	case 3:
		p.Parity = MARKPARITY; //Mark verification
		break;
	}

	switch(stopbit) //Stop bit
	{
	case 1:
		p.StopBits = ONESTOPBIT; //1 stop bit
		break;
	case 2:
		p.StopBits = TWOSTOPBITS; //2-bit stop bit
		break;
	case 3:
		p.StopBits = ONE5STOPBITS; //1.5 stop bit
		break;
	}

	if(! SetCommState(hCom, &p))
	{
		// set parameters failed
		return false;
	}

	//Timeout processing in milliseconds
	//Total timeout = time factor × Number of characters read or written + time constant
	COMMTIMEOUTS TimeOuts;
	TimeOuts.ReadIntervalTimeout = 1000; //Read interval timeout
	TimeOuts.ReadTotalTimeoutMultiplier = 500; //Reading time coefficient
	TimeOuts.ReadTotalTimeoutConstant = 5000; //Read time constant
	TimeOuts.WriteTotalTimeoutMultiplier = 500; // Write time coefficient
	TimeOuts.WriteTotalTimeoutConstant = 2000; //Write time constant
	SetCommTimeouts(hCom,&TimeOuts);

	PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);//Clear serial port buffer

	memcpy(pHandle, &hCom, sizeof(hCom));// Save handle

	return true;
}

void WzSerialPort::close()
{
	HANDLE hCom = *(HANDLE*)pHandle;
	CloseHandle(hCom);
}

int WzSerialPort::send(const void *buf,int len)
{
	HANDLE hCom = *(HANDLE*)pHandle;

	if (this->synchronizeflag)
	{
		// Synchronization mode
		DWORD dwBytesWrite = len; //Number of data bytes successfully written
		BOOL bWriteStat = WriteFile(hCom, //Serial port handle
									buf, //Data header address
									dwBytesWrite, //Number of data bytes to send
									&dwBytesWrite, //DWORD *, used to receive and return the number of data bytes successfully sent
									NULL); //NULL means synchronous transmission, OVERLAPPED * means asynchronous transmission
		if (!bWriteStat)
		{
			return 0;
		}
		return dwBytesWrite;
	}
	else
	{
		//Asynchronous mode
		DWORD dwBytesWrite = len; //Number of data bytes successfully written
		DWORD dwErrorFlags; //Error flag
		COMSTAT comStat; //Communication status
		OVERLAPPED m_osWrite; //Asynchronous I / O structure

		//Create an event handler for OVERLAPPED, which will not really be used, but the system requires it
		memset(&m_osWrite, 0, sizeof(m_osWrite));
		m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, L"WriteEvent");

		ClearCommError(hCom, &dwErrorFlags, &comStat); //Clear the communication error and obtain the current status of the device
		BOOL bWriteStat = WriteFile(hCom, //Serial port handle
			buf, //Data header address
			dwBytesWrite, //Number of data bytes to send
			&dwBytesWrite, //DWORD *, used to receive and return the number of data bytes successfully sent
			&m_osWrite); //NULL means synchronous transmission, OVERLAPPED * means asynchronous transmission
		if (!bWriteStat)
		{
			if (GetLastError() == ERROR_IO_PENDING) //If the serial port is writing
			{
				WaitForSingleObject(m_osWrite.hEvent, 1000); //Wait for the write event for 1 second
			}
			else
			{
				ClearCommError(hCom, &dwErrorFlags, &comStat); //Clear communication error
				CloseHandle(m_osWrite.hEvent); //Close and free haevent memory
				return 0;
			}
		}
		return dwBytesWrite;
	}
}

int WzSerialPort::receive(void *buf,int maxlen)
{
	HANDLE hCom = *(HANDLE*)pHandle;

	if (this->synchronizeflag)
	{
		//Synchronization mode
		DWORD wCount = maxlen; //Number of data bytes successfully read
		BOOL bReadStat = ReadFile(hCom, //Serial port handle
									buf, //Data header address
									wCount, //Maximum number of bytes of data to read
									&wCount, //DWORD *, used to receive and return the number of data bytes successfully read
									NULL); //NULL means synchronous transmission, OVERLAPPED * means asynchronous transmission
		if (!bReadStat)
		{
			return 0;
		}
		return wCount;
	}
	else
	{
		//Asynchronous mode
		DWORD wCount = maxlen; //Number of data bytes successfully read
		DWORD dwErrorFlags; //Error flag
		COMSTAT comStat; //Communication status
		OVERLAPPED m_osRead; //Asynchronous I / O structure

		//Create an event handler for OVERLAPPED, which will not really be used, but the system requires it
		memset(&m_osRead, 0, sizeof(m_osRead));
		m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, L"ReadEvent");

		ClearCommError(hCom, &dwErrorFlags, &comStat); //Clear the communication error and obtain the current status of the device
		if (!comStat.cbInQue)return 0; //Returns false if the number of input buffer bytes is 0

		BOOL bReadStat = ReadFile(hCom, //Serial port handle
			buf, //Data header address
			wCount, //Maximum number of bytes of data to read
			&wCount, //DWORD *, used to receive and return the number of data bytes successfully read
			&m_osRead); //NULL means synchronous transmission, OVERLAPPED * means asynchronous transmission
		if (!bReadStat)
		{
			if (GetLastError() == ERROR_IO_PENDING) //If the serial port is reading
			{
				//The last parameter of the GetOverlappedResult function is set to TRUE
				//The function waits until the read operation completes or returns due to an error
				GetOverlappedResult(hCom, &m_osRead, &wCount, TRUE);
			}
			else
			{
				ClearCommError(hCom, &dwErrorFlags, &comStat); //Clear communication error
				CloseHandle(m_osRead.hEvent); //Close and free the memory of heevent
				return 0;
			}
		}
		return wCount;
	}
}

WzSerialPort.h

#ifndef _WZSERIALPORT_H
#define _WZSERIALPORT_H

/*
	Author: Ouyang Wei
	Date: December 14, 2017
	Class name: WZSerialPort
	Purpose: serial port reading and writing
	Example:
		Refer to main cpp	
*/

class WzSerialPort
{
public:
	WzSerialPort();
	~WzSerialPort();

	// When the serial port is opened, true is returned for success and false is returned for failure
	// Portname (serial port name): under Windows, it is "COM1", "com2", etc.; under Linux, it is "/ dev/ttyS1", etc
	// Baudrate: 9600, 19200, 38400, 43000, 56000, 57600, 115200 
	// Parity: 0 means no parity, 1 means odd parity, 2 means even parity, and 3 means mark parity (only applicable to windows)
	// Data bit: 4-8(windows),5-8(linux), usually 8 bits
	// Stopbit: 1 is the 1-bit stop bit, 2 is the 2-bit stop bit, and 3 is the 1.5-bit stop bit
	// Synchronizeflag (synchronous, asynchronous, only applicable to windows): 0 is asynchronous, 1 is synchronous
	bool open(const char* portname, int baudrate, char parity, char databit, char stopbit, char synchronizeflag=1);

	//Close the serial port, parameters to be determined
	void close();

	//When sending data or writing data, the length of the sent data is returned successfully, and 0 is returned if it fails
	int send(const void *buf,int len);

	//Accept data or read data, successfully return the length of reading actual data, and fail to return 0
	int receive(void *buf,int maxlen);

private:
	int pHandle[16];
	char synchronizeflag;
};

#endif

main.cpp

#include <iostream>
#include "WzSerialPort.h"
#include <windows.h>

using namespace std;

void sendDemo()
{
	WzSerialPort w;
	if (w.open("COM1", 115200, 0, 8, 1)) //Open COM1 port, baud rate 115200
	{
	     cout << "open com1 success!" << endl;
		for (int i = 0;i < 10;i++)
		{
			while(1) {
				w.send("wuliyuan", 8);
				Sleep(1000);
			}
		}
		cout << "send demo finished...";
	}
	else
	{
		cout << "open serial port failed...";
	}
}

void receiveDemo()
{
	WzSerialPort w;
	if (w.open("COM1", 115200, 0, 8, 1))//Open COM1 port, baud rate 115200
	{
		char buf[1024];
		while (true)
		{
			memset(buf, 0,1024);
			w.receive(buf, 1024);
			cout << buf << endl;
		}
	}
}

int main(int argumentCount, const char* argumentValues[]) 
{
	// Suppose COM1 has been connected to another serial port

	// Send demo
	sendDemo();

	// Receive demo
	//receiveDemo();

	getchar();
	return 0;
}

Source code analysis

It can be seen that the serial communication module includes wzserialport CPP and wzserialport H two files. When we need to use them, we only need to add them to our project and include the header file when we need to use them.

example

First use VSPD to open two virtual serial ports

Because we use the program to open COM1, we use the serial port debugging assistant to open COM2.

Next, let's test the sending function, comment out the receiveDemo() in the main function, and test the sending function regardless of the reception.

Run the program as shown below

The serial port debugging assistant is displayed as follows. You can see that the receiving data area continuously receives the data sent by the program

Next, we continue to test the receiving function, comment out the sendDemo function in the main function and cancel the comment of the receiveDemo function.

In addition, we also need to start the cycle sending and set the sending content in the serial port debugging assistant of COM2 port, and click send.

Run the program.

Conclusion: the serial communication module can be used directly by adding it to the project we use.

Keywords: C C++

Added by cap2cap10 on Thu, 30 Dec 2021 13:39:34 +0200