serial_port_web_logo

SerialPort C++ library

v3.2.0

Table of contents

Overview

The SerialPort C++ library provides a simple interface for working with serial ports. The SerialPort.h file contains the declaration of the SerialPort C++ class. SerialPort includes functions to open serial ports, write data to them, and read data from them. The SerialPort library also provides applications to test communication with any equipment (send data and check responses). It requires the C++17 standard. The library has no third-party dependencies and is compatible with Linux and Windows operating systems. The library is licensed under the Apache 2.0 license.

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 14.10.2021 First version
2.0.0 20.05.2022 - Interface changed.
- Added method for RTS/CTS hardware flow control.
2.1.0 10.07.2022 - Code optimized.
2.2.0 22.03.2023 - Code optimized.
- Documentation updated.
2.3.0 24.04.2023 - Added new test application.
2.4.0 19.06.2023 - Added new test application (SerialPortStringTester).
2.5.0 26.06.2023 - Updated test applications (SerialPortStringTester and SerialPortTester combined into one application).
3.0.0 27.06.2023 - Changed interface (added new methods read(…) and write(…) instead of readData(…) and sendData(…)).
3.0.1 19.07.2023 - Fixed compilation error in methods read(…) and write(…) for Linux.
3.0.2 17.12.2023 - Methods description updated.
- Documentation updated.
- Default branch name changed from “main” to “master”.
3.0.3 26.03.2024 - Test application updated.
- Documentation updated.
3.0.4 22.05.2024 - Documentation updated.
3.0.5 30.05.2024 - SBUS protocol baudrate (100000) added for Linux.
- Wait data timeout mechanism changed for Linux.
3.1.0 12.07.2024 - Synchronized behavior on Linux and Windows.
- CMake updated.
3.2.0 16.10.2025 - Added custom baudrate support for Linux and Windows.

Library files

The library is supplied only by source code. The user is given a set of files in the form of a CMake project (repository). The repository structure is shown below:

CMakeLists.txt -------------- Main CMake file of the library.
src ------------------------- Folder with library source code.
    Impl -------------------- Folder with serial port implementation files.
        SerialPortImpl.h ---- Header file of serial port implementation.
        SerialPortImpl.cpp -- C++ implementation file.
        CustomBaudrate.h ---- Header file of serial port implementation.
        CustomBaudrate.cpp -- C++ implementation file.
    CMakeLists.txt ---------- CMake file of the library.
    SerialPort.h ------------ Main library header file.
    SerialPortVersion.h ----- Header file with library version.
    SerialPortVersion.h.in -- Service CMake file to generate version header.
    SerialPort.cpp ---------- C++ implementation file.
examples -------------------- Folder with examples.
    CMakeLists.txt ---------- CMake file to include examples.
    SerialPortDataReceiver -- Folder with data receiver example.
        CMakeLists.txt ------ CMake file of data receiver example.
        main.cpp ------------ Source code of data receiver example.
    SerialPortDataSender ---- Folder with data sender example.
        CMakeLists.txt ------ CMake file of data sender example.
        main.cpp ------------ Source code of data sender example.
    SerialPortTester -------- Folder with serial port tester application.
        CMakeLists.txt ------ CMake file of serial port tester application.
        main.cpp ------------ Source code of serial port tester application.

SerialPort class description

SerialPort class declaration

SerialPort class declared in SerialPort.h file. Class declaration:

namespace cr
{
namespace clib
{
/// Serial port class.
class SerialPort
{
public:

    /// Get string of current library version.
    static std::string getVersion();

    /// Class constructor.
    SerialPort();

    /// Class destructor.
    ~SerialPort();

    /// Open serial port.
    bool open(std::string file, unsigned int baudrate,
              unsigned int timeoutMsec = 100, const char *mode = "8N1");

    /// Read data from serial port.
    int read(uint8_t *buf, uint32_t size);

    /// Write data to serial port.
    int write(uint8_t *buf, uint32_t size);

    /// Get open status.
    bool isOpen();

    /// Close serial port.
    void close();

    /// Set RTS/CTS hardware flow control.
    bool setFlowControl(bool enable);
};
}
}

getVersion method

The getVersion() method returns string of current class version. Method declaration:

static std::string getVersion();

Method can be used without SerialPort class instance:

cout << "Serial port version: " << SerialPort::getVersion() << endl;

Console output:

Serial port version: 3.2.0

open method

The open(…) method opens a serial port. If a serial port is already open, the method will first close the serial port and then attempt to open it again according to the new parameters. Method declaration:

bool open(std::string file, unsigned int baudrate, unsigned int timeoutMsec = 100, const char *mode = "8N1");
Parameter Value
file Serial port device name. Format depends on OS. For Linux OS format: “/dev/serial/by-id/…”, “/dev/ttyS0”, “/dev/ttyUSB0” etc. For Windows OS format “\\.\COM0” etc.
baudrate Baudrate. For example 115200.
timeoutMsec Wait data timeout, milliseconds.
mode Mode. Always 3 symbols: 1 - Number of bits (8, 7, 6, 5), 2 - parity (N, E, O), 3 - number of stop bits (1 or 2). Example: “8N1”.

Returns: TRUE if the serial port opened successfully or FALSE if not.

read method

The read(…) method reads data from the serial port. When a user calls the read(…) method, it will check if there is any data in the serial port buffer and return the data if it exists. If no data exists in the serial port, the read(…) method will wait for a maximum of timeoutMsec milliseconds (set by the user in the open(…) method) until data arrives, or return a negative result if no data is received within the timeout period. If you don’t want to wait and just want to check for data in the serial port, set timeoutMsec = 0 in the open(…) method. Method declaration:

int read(uint8_t *buf, uint32_t size);
Parameter Value
buf Pointer to buffer.
size Size of buffer. Size of data that you want to read from the serial port. The method will return <= size bytes from the input serial port buffer.

Returns: Number of received bytes <= size, or 0 if no data is in the input serial port buffer, or -1 if the serial port is not open.

write method

The write(…) method is intended to send data to serial port. Method declaration:

int write(uint8_t *buf, uint32_t size);
Parameter Value
buf Pointer to buffer with data to send.
size Number of bytes to send.

Returns: Number of bytes sent <= size, or -1 if the serial port is not open.

isOpen method

The isOpen() method is intended to obtain the serial port connection status. Method declaration:

bool isOpen();

Returns: TRUE if the serial port is open or FALSE if not.

close method

The close() method is intended to close the serial port. This method will close the serial port if it is open. Method declaration:

void close();

setFlowControl method

The setFlowControl(…) method is intended to set RTS/CTS hardware flow control. RTS / CTS Flow Control is another flow control mechanism that is part of the RS232 standard. It makes use of two further pins on the RS232 connector, RTS (Request to Send) and CTS (Clear to Send). These two lines allow the receiver and the transmitter to alert each other to their state. Method declaration:

bool setFlowControl(bool enable);
Parameter Value
enable Enable RTS/CTS hardware flow control flag: true - enable, false - disable.

Returns: TRUE if mode was changed or FALSE.

Examples

Data sender example

#include <iostream>
#include <chrono>
#include <ctime>
#include <thread>
#include "SerialPort.h"

using namespace std;
using namespace cr::clib;
using namespace std::chrono;

int main(void)
{
    cout << "Data sender example v" << SerialPort::getVersion() << endl;

    string portName = "";
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__)
    cout << "Enter serial port name (e.g. /dev/ttyS0): ";
    cin >> portName;
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
    cout << "Enter serial port num (1, 2 ..): ";
    cin >> portName;
    portName = "\\\\.\\COM" + portName;
#endif
    
    int portBaudrate = 115200;
    cout << "Enter serial port baudrate: ";
    cin >> portBaudrate;

    int numBytesToSend = 10;
    cout << "Enter num bytes to send: ";
    cin >> numBytesToSend;
    if (numBytesToSend > 1024)
        return -1;

    int cyclePeriodMs = 0;
    cout << "Enter sending data period ms: ";
    cin >> cyclePeriodMs;

    // Open serial port.
    SerialPort serialPort;
    if (!serialPort.open(portName, portBaudrate))
        return -1;
    
    // Main loop.
    uint8_t outputData[1024];
    time_point<system_clock> start = system_clock::now();
    while (true)
    {
        // Wait according to the specified period.
        int waitTime = cyclePeriodMs -
        (int)duration_cast<milliseconds>(system_clock::now() - start).count();
        while (waitTime > 0)
            waitTime = cyclePeriodMs -
            (int)duration_cast<milliseconds>(system_clock::now() - start).count();
        start = system_clock::now();

        // Prepare random data.
        for (int i = 0; i < numBytesToSend; ++i)
            outputData[i] = (uint8_t)(rand() % 255);

        // Send data.
        cout << serialPort.write(outputData, numBytesToSend) <<
        " bytes sent" << endl;
    }

    return 1;
}

Data receiver example

#include <iostream>
#include <chrono>
#include <ctime>
#include <thread>
#include "SerialPort.h"

using namespace std;
using namespace cr::clib;
using namespace std::chrono;

int main(void)
{
    cout << "Data receiver example v" << SerialPort::getVersion() << endl;

    string portName = "";
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__)
    cout << "Enter serial port name (e.g. /dev/ttyS0): ";
    cin >> portName;
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
    cout << "Enter serial port num (1, 2 ..): ";
    cin >> portName;
    portName = "\\\\.\\COM" + portName;
#endif

    int portBaudrate = 115200;
    cout << "Enter serial port baudrate: ";
    cin >> portBaudrate;

    int waitDataTimeoutMs = 0;
    cout << "Enter wait data timeout, msec: ";
    cin >> waitDataTimeoutMs;

    // Open serial port.
    SerialPort serialPort;
    if (!serialPort.open(portName, portBaudrate, waitDataTimeoutMs))
        return -1;

    // Main loop.
    uint8_t inputData[1024];
    while (true)
    {
        // Read data.
        int bytes = serialPort.read(inputData, 1024);

        if (bytes <= 0)
            cout << "No input data" << endl;
        else
            cout << bytes << " bytes read." << endl;
    }
    return 1;
}

Build and connect to your project

Typical commands to build SerialPort library (on Linux OS):

cd SerialPort
mkdir build
cd build
cmake ..
make

If you want connect SerialPort library to your CMake project as source code you can make follow. For example, if your repository has structure:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp

Create 3rdparty in your repository folder and copy SerialPort repository folder to 3rdparty folder of your repository. New structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    SerialPort

Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should contain:

cmake_minimum_required(VERSION 3.13)

################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)

################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# To inherit the top-level architecture when the project is used as a submodule.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
# Disable self-overwriting of parameters inside included subdirectories.
SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE)

################################################################################
## CONFIGURATION
## 3rd-party submodules configuration
################################################################################
SET(${PARENT}_SUBMODULE_SERIAL_PORT                     ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_SERIAL_PORT)
    SET(${PARENT}_SERIAL_PORT                           ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_SERIAL_PORT_EXAMPLES                  OFF CACHE BOOL "" FORCE)
endif()

################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_SERIAL_PORT)
    add_subdirectory(SerialPort)
endif()

File 3rdparty/CMakeLists.txt adds folder SerialPort to your project and excludes examples (SerialPort examples) from compiling (by default examples excluded from compiling if SerialPort included as sub-repository). Your repository new structure will be:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    SerialPort

Next you need include folder 3rdparty in main CMakeLists.txt file of your repository. Add string at the end of your main CMakeLists.txt:

add_subdirectory(3rdparty)

Next you have to include SerialPort library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} SerialPort)

Done!

SerialPortTester application

SerialPortTester is an application designed to test communication with any equipment via a serial port. The application allows users to send any data (manual input) to the serial port and check the response from the equipment. The application supports data entry in both HEX and string formats (for ASCII protocols). The application supports Windows and Linux operating systems. How to use:

Copy the executable file SerialPortTester and make it executable (on Linux):

sudo chmod +x SerialPortTester

Run the application with sudo (the application doesn’t have parameters):

sudo ./SerialPortTester

You will see a dialog to enter the serial port name. On Windows OS you should set the COM port number (the application will create the serial port name according to Windows format ”\\\\.\\COM” + portNum”):

================================================
Serial port tester v3.3.0
================================================

Set COM port num (1,2,3,...): 2

On Linux OS you have to type the full serial port name: /dev/ttyUSB0,1(N) (for USB to serial adapters), /dev/ttyS0,1(N) (for built-in serial ports), /dev/serial/by-id/(port name) (for USB to serial adapters) and press “Enter” on the keyboard:

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/ttyUSB0

Next, you have to set the baudrate:

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/ttyUSB0

After that, you have to set the baudrate and press “Enter” on the keyboard:

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/ttyUSB0
Set baudrate: 115200

Next, you have to set the wait data timeout (after sending data, the application will wait for a response) and press “Enter” on the keyboard:

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/ttyUSB0
Set baudrate: 115200
Set wait data timeout (msec): 2000

After that, you have to set and choose the mode: string mode (you will be able to input text for ASCII protocols) or HEX mode (you will be able to input HEX values):

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/ttyUSB0
Set baudrate: 115200
Set wait data timeout (msec): 2000
Chose mode (1 - string, 0 - HEX): 0

After that, you will be able to enter a message to send. In HEX mode, you have to input a string in HEX format and press “Enter” on the keyboard. The application will send your message to the serial port and wait for the timeout period. After the timeout, the application will check for a response and display it, or show the “ERROR: No response from serial port” message:

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/ttyUSB0 
Set baudrate: 115200
Set wait data timeout (msec): 2000
Chose mode (1 - string, 0 - HEX): 0
Enter HEX string (e.g. AAF00A): AA040170001FEBAA
[TX]: aa 4 1 70 0 1f eb aa 
[RX]: 55 17 70 33 46 54 49 49 36 34 30 30 30 31 30 30 30 30 30 58 45 50 4e 0 91 eb aa 
Enter HEX string (e.g. AAF00A): AA0401710020EBAA
[TX]: aa 4 1 71 0 20 eb aa 
[RX]: 55 17 71 33 42 31 31 36 30 31 34 31 0 0 0 0 0 0 0 0 0 0 0 0 b0 eb aa 
Enter HEX string (e.g. AAF00A): 

In string mode, the application will show both the HEX data and the string representation of the response.

================================================
Serial port tester v3.3.0
================================================

Set serial port name: /dev/serial/by-id/usb-FTDI_USB-RS232_Cable_FT5MJ4PE-if00-port0
Set baudrate: 115200
Set wait data timeout (msec): 2000
Chose mode (1 - string, 0 - HEX): 1
Enter string: TestMessage
[TX]: 54 65 73 74 4d 65 73 73 61 67 65 d a 
ERROR: No response from serial port
Enter string: ZR
[TX]: 5a 52 d a
[RX]: 5a 52 3d 31 d a  :  ZR=1

In string mode, the application automatically adds the symbols ‘\r’ and ‘\n’ at the end of a string.


Table of contents