CSerialPort: one C++ serial port core with bindings for seven languages
CSerialPort - lightweight cross-platform serial port library and protocol parsing for C++/C/C#/Java/Python/Node.js/Electron/Rust
At a glance
- What is it?
- CSerialPort is a lightweight cross-platform serial port library written in C++, with protocol parsing, hot plug notification, sync and async transfer, and bindings for C, C#, Java, Python, Node.js, Electron and Rust. It covers Windows, Linux, macOS, Android and FreeBSD, and it ships a long list of cross-compilation toolchain files.
- Who is it for?
- CSerialPort suits a team that needs serial port access from several languages at once, or that needs a serial core they can compile into firmware images for arm, aarch64, mips64el and riscv64 targets, and the protocol parser and hot plug listener are the pieces that are hard to reimplement well.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 126 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A C++ core wrapped by bindings for eight languages
CSerialPort is described as a lightweight cross-platform serial port library based on C++, which can read and write serial ports and parse communication protocols on multiple operating systems. It also supports C, C#, Java, Python, Node.js, Electron and Rust.
The design is a single core with a language surface around it. The repository layout makes that visible: a lib/ directory with the implementation, an include/ directory with the public headers, a src/ directory, and a bindings/ directory holding the wrappers. The include path contains IProtocolParser.h and SerialPortListener.h, so the two extension points are named interfaces rather than internal details.
The stated design principles are cross-platform, easy to use, higher efficiency and custom protocol parsing. The feature list expands them: cross-platform across Windows, Linux, macOS, Android and FreeBSD; sync and async read and write; custom protocol parsing through IProtocolParser and the CommNoGuiProtocol example; hot plug notify through CSerialPortHotPlugListener; and the third party language bindings.
The hot plug listener is the feature most serial libraries leave out. If a USB to serial adapter is unplugged and replugged, an application with a listener can react rather than discovering the dead handle on the next write. For a device that has to run unattended, that is the difference between a reconnect and a manual reboot.
The protocol parser is the other one. Serial protocols in the wild are framed, with a header, a length and a checksum, and every project ends up writing a parser that handles partial reads and resynchronisation. Making that a pluggable interface means the framing logic is written once and tested independently of the transport.
Building it with CMake and running the loopback demo
The quick start is a clone and a CMake build.
$ git clone --depth=1 https://github.com/itas109/CSerialPort.git
$ cd CSerialPort
$ mkdir bin && cd bin
$ cmake ..
$ cmake --build .The out-of-source build into a bin directory is the layout the cross-compilation instructions also use, with one directory per target, which keeps the seven toolchain builds from colliding in the source tree.
The demo output in the documentation is worth reading, because it shows what the library reports and how much detail you get.
[INFO] openPort - portName: /dev/ttyCH341USB0, baudRate: 9600, dataBit: 8, parity: 0, stopBit: 1, flowControl: 0, mode: async, readBufferSize:4096(4096), readIntervalTimeoutMS: 0, minByteReadNotify: 1, byteReadBufferFullNotify: 3276That single line is the whole configuration surface in practice: baud rate, data bits, parity, stop bits, flow control, sync or async mode, read buffer size, interval timeout in milliseconds, the minimum byte count that triggers a notify, and the buffer-full threshold. The demo then writes a five byte payload, reads back twelve bytes, and prints them both as a string and as hex.
The output also shows the library identifies the adapter by USB vendor and product ID, listing /dev/ttyCH341USB0 as a QinHeng CH340 serial converter with the 1a86:7523 identifier, and separately listing a pty terminal. That kind of enumeration is what a user-facing tool needs to offer a port picker, and the library gives you the friendly name and the hardware identity rather than just a device path.
The demo is described as a serial port lookback test on Linux, so it exercises the full loop: enumerate, open, write, read, decode.
Protocol parsing, async mode and the character interval timeout
The Todo list in the documentation is unusually informative because it records what was finished and what was not.
Under the long-term goal, supporting a modern c++11 standard is checked off, and so is support for character interval timeout and custom protocol parsing. Item three is the interesting one: support raw sync, raw async and extend sync mode, where raw sync and raw async are marked implemented and extend sync mode is explicitly not implemented.
That is a direct statement that the sync mode is less complete than the async mode. In a sync read you ask for bytes and block until they arrive or a timeout expires, and the extended sync mode, presumably a blocking read that returns whatever has arrived rather than requiring a fixed count, is not there. Async mode is the more capable of the two here, which is worth knowing before you design around synchronous calls.
The character interval timeout is the other key mechanism. A serial link that sends one character at a time with a gap between them has no message boundaries on the wire, so the receiver has to infer them from timing. A character interval timeout says that a gap longer than N milliseconds ends the current message. The openPort line shows readIntervalTimeoutMS: 0 in the demo, which disables it, and minByteReadNotify: 1 sets the smallest number of bytes that counts as worth waking the reader for.
The short-term goals show the same pattern of a layered abstraction being built out. A cross-platform OS identify class, a cross-platform thread class, a cross-platform lock class and a cross-platform higher efficiency timer class are all checked off. A cross-platform thread pool class and a performance test report covering throughput, delay and packet dropout rates are not done yet.
That last item is the one to notice. The absence of a published performance report means the efficiency claim in the design principles has no published numbers behind it in this repository, so if throughput matters for your device you would need to measure it yourself rather than take the claim on trust.
Cross compilation for arm, aarch64, mips64el and riscv64
The cross-compilation section is longer than the quick start, and that tells you who this library is really for: anyone shipping a C++ binary to a Linux device.
The pattern is identical each time: install the cross compiler package, create a build directory, and point CMake at a toolchain file from the cmake/ directory.
For arm on Ubuntu 22.04:
$ sudo apt-get install g++-arm-linux-gnueabi
$ cd CSerialPort
$ mkdir bin_arm && cd bin_arm
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=./cmake/toolchain_arm.cmake
$ cmake --build .The same shape covers arm on Windows, aarch64 on Ubuntu and on Windows, mips64el on Ubuntu, riscv64 on Ubuntu, and mingw64 with Windows POSIX threads on Ubuntu. The Windows targets use the Unix Makefiles generator explicitly with -G "Unix Makefiles", because the host is Unix and the target is Windows, and the Windows toolchain file is named toolchain_arm_win32.cmake or toolchain_aarch64_win32.cmake to match.
That is seven documented cross builds, each needing its own binary directory. A library that ships this many toolchain files is anticipating embedded Linux and router firmware work, which is the context where a serial port matters and where the language bindings matter less.
The build system is not CMake alone. The repository also carries an xmake.lua at the root and an examples/xmake.lua, so xmake is a supported alternative build path, alongside a cserialport-config.cmake.in for install and a CMakeLists.txt at the root. The .clang-format and .clang-tidy files plus a codeformat.sh script indicate clang tooling is expected, and cserialport.cppcheck is a cppcheck configuration.
Install paths: vcpkg, package managers and the GUI examples
There are two ways to get the library into a project.
The first is vcpkg, Microsoft's dependency manager, which supports the library as a port.
$ git clone https://github.com/Microsoft/vcpkg.git
$ cd vcpkg
$ ./bootstrap-vcpkg.sh
$ ./vcpkg install cserialportThe second is to vendor the source and build it as part of your own CMake project, using the out-of-source pattern shown earlier, and there is a cserialport-config.cmake.in template for producing a findable package config when you install it.
The examples directory shows the range of front ends that exist for it: CommQT for a Qt desktop application, CommElectron for an Electron app, CommMFC for a Windows MFC dialog, CommNoGui for a console program, CommWeb for a browser based interface, CommWebServer for a server, CommWebview for a webview host, CommAndroid for Android, and CommNoGuiProtocol as the protocol parsing example referenced from the feature list. There is a separate cross-platform GUI serial port tool called CommMaster linked from the documentation.
So the library is not only a C++ library with bindings. It is also the engine behind several complete applications, and the Electron and Web examples show the pattern that matters for web-adjacent hardware work: a native serial core on the machine that has the port, with a browser or webview interface on top.
The documentation set is in the doc/ directory and includes an API document as a Windows help file, a directory list document, an error guide and a FAQ. An error guide is a good sign for a hardware-facing library, because serial failures are a matter of codes and line settings rather than exceptions, and having the codes documented separately from the API reference saves a lot of time.
LGPLv3 with a linking exception, and what that means for a native dependency
The licence badge in the documentation reads License-LGPLv3 with linking exception, and the repository carries a LICENSE file. The licence field on the repository itself says Other, a custom licence GitHub cannot classify, which is what happens with an LGPL plus linking exception variant.
The linking exception is the part that matters for adoption. LGPLv3 on its own is designed so that a proprietary application can dynamically link against the library without that application becoming subject to the LGPL. The linking exception formalises that for this code. In practice, for a team whose application is closed source and who wants to ship a serial library inside it, this is the arrangement that makes the dependency workable, and it is why the badge calls it out explicitly rather than just saying LGPLv3.
This is not legal advice, and the precise effect of the exception depends on the exact text in the LICENSE file, so anyone shipping commercially should read it. But the practical summary is that the licence was chosen with embedding in mind, and it is more permissive for a closed-source application than a plain LGPL would be.
The bindings directory is worth a note for the same reason. A binding is a compiled shared library that an application links or loads, and the licence question of that binding is part of your own decision about whether you can ship it. If you are using the Python, Node.js or Rust binding rather than the C++ core, that is the artefact you need to reason about.
On maintenance, the release history shows v5.0.0 tagged on 2026-06-19 with v4.3.3 from 2025-09-25 and v4.3.2 from 2025-02-10 before it. The last push was on 2026-06-19, the same day as the v5.0.0 tag, so the newest release and the current tree match. The version string in the build output is 5.0.0.260619, which encodes both the release and its date.
A major version bump at 5.0.0 for a library with this many users suggests interface changes that would break existing code, so an upgrade should be read rather than applied on a schedule.
Editorial conclusion
CSerialPort suits a team that needs serial port access from several languages at once, or that needs a serial core they can compile into firmware images for arm, aarch64, mips64el and riscv64 targets, and the protocol parser and hot plug listener are the pieces that are hard to reimplement well. It does not suit a Python developer who only needs to open a port, because the binding adds a native dependency for what a mature Python package does directly, and it does not suit a Windows MSComm shop expecting COM port semantics to match exactly. Before adopting it, read the error guide and FAQ in the doc/ directory, and note that the licence is LGPLv3 with a linking exception, which is what makes the closed-source embedding story workable.
Frequently asked questions
How do I build CSerialPort from source?
Clone the repository, create a bin directory, and run cmake .. followed by cmake --build . from inside it. The build is CMake based and the out-of-source bin directory layout is the same one used for the cross-compilation targets.
Which languages can I use CSerialPort from?
The library is built in C++ and provides third party language bindings for C, C#, Java, Python, Node.js, Electron and Rust, with the binding sources under the bindings/ directory in the repository.
Which operating systems does CSerialPort support?
Windows, Linux, macOS, Android and FreeBSD, according to the feature list. The documented cross-compilation targets add arm, aarch64, mips64el and riscv64 Linux builds plus Windows targets built from a Unix host with mingw64.
What is the character interval timeout used for in CSerialPort?
It defines a gap in the received data that ends the current message, which is how a receiver infers message boundaries on a link that sends characters one at a time. The openPort configuration exposes it as readIntervalTimeoutMS, alongside minByteReadNotify and byteReadBufferFullNotify.
What licence does CSerialPort use?
LGPLv3 with a linking exception, according to the licence badge in the documentation, with the LICENSE file in the repository root. The repository's licence field reads Other because GitHub cannot classify that variant.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/itas109-cserialport)Was this useful?