-
Notifications
You must be signed in to change notification settings - Fork 0
Advanced features #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
24a0691
f0b6881
88149e6
ecca33e
93d603b
f2c3d92
fc3e54a
a779b3b
dd728f4
1cc1838
e843a65
7794e36
bb2e1ce
b9f6dda
0f3ade2
2420b65
9d5476e
c523f11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,4 +1,4 @@ | ||||||
name: Build & Release CPP-Unix-Bindings | ||||||
name: Build & Release cpp_unix_bindings | ||||||
|
||||||
on: | ||||||
push: | ||||||
|
@@ -12,34 +12,24 @@ jobs: | |||||
build: | ||||||
name: Build shared library | ||||||
runs-on: ubuntu-latest | ||||||
env: | ||||||
CC: clang | ||||||
CXX: clang++ | ||||||
|
||||||
steps: | ||||||
- name: Checkout repository | ||||||
uses: actions/checkout@v3 | ||||||
|
||||||
- name: Install build dependencies | ||||||
- name: Build in manylinux2014 container | ||||||
run: | | ||||||
sudo apt-get update | ||||||
sudo apt-get install -y build-essential cmake clang make | ||||||
|
||||||
- name: Configure CMake | ||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release | ||||||
|
||||||
- name: Compile | ||||||
run: cmake --build build -j $(nproc) | ||||||
docker run --rm -v "${{ github.workspace }}:/project" -w /project quay.io/pypa/manylinux2014_x86_64 /bin/bash -c "yum install -y cmake3 devtoolset-11-gcc devtoolset-11-gcc-c++ && ln -sf /usr/bin/cmake3 /usr/bin/cmake && source /opt/rh/devtoolset-11/enable && cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j\$(nproc)" | ||||||
|
||||||
- name: Upload library artifact | ||||||
uses: actions/upload-artifact@v4 | ||||||
with: | ||||||
name: libCPP-Unix-Bindings | ||||||
path: build/libCPP-Unix-Bindings.so | ||||||
name: libcpp_unix_bindings | ||||||
path: build/libcpp_unix_bindings.so | ||||||
retention-days: 14 | ||||||
|
||||||
- name: Attach library to release | ||||||
if: github.event_name == 'release' | ||||||
uses: softprops/action-gh-release@v1 | ||||||
with: | ||||||
files: build/libCPP-Unix-Bindings.so | ||||||
files: build/libcpp_unix_bindings.so | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
.vscode/ | ||
build/ | ||
src/version_config.cpp | ||
src/version_config.h | ||
.cache/ | ||
compile_commands.json |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,12 +1,19 @@ | ||||||
cmake_minimum_required(VERSION 3.30) | ||||||
|
||||||
set(VERSION_MAJOR 0) | ||||||
set(VERSION_MINOR 2) | ||||||
set(VERSION_MAJOR 1) | ||||||
set(VERSION_MINOR 0) | ||||||
set(VERSION_PATCH 0) | ||||||
|
||||||
set(PROJECT_N CPP-Unix-Bindings) | ||||||
set(PROJECT_N cpp_unix_bindings) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
project(${PROJECT_N} VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) | ||||||
|
||||||
# Ensure position-independent code for all targets (required for shared libraries) | ||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON) | ||||||
|
||||||
# Always use pthread | ||||||
add_link_options(-pthread) | ||||||
add_compile_options(-pthread) | ||||||
|
||||||
# Generate compile_commands.json for clang-based tooling (clangd / clang-tidy) | ||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | ||||||
|
||||||
|
@@ -22,6 +29,16 @@ CPMAddPackage( | |||||
OPTIONS "GTEST_BUILD_DOCS OFF" | ||||||
) | ||||||
|
||||||
set(CPP_CORE_VERSION_MAJOR ${VERSION_MAJOR} CACHE STRING "") | ||||||
set(CPP_CORE_VERSION_MINOR ${VERSION_MINOR} CACHE STRING "") | ||||||
set(CPP_CORE_VERSION_PATCH ${VERSION_PATCH} CACHE STRING "") | ||||||
|
||||||
CPMAddPackage( | ||||||
NAME cpp_core | ||||||
GITHUB_REPOSITORY Serial-IO/cpp-core | ||||||
GIT_TAG main | ||||||
) | ||||||
|
||||||
# Ensure clang tooling can pick up compile_commands.json from project root | ||||||
if(CMAKE_EXPORT_COMPILE_COMMANDS) | ||||||
add_custom_target(copy-compile-commands ALL | ||||||
|
@@ -35,12 +52,13 @@ endif() | |||||
set(CMAKE_CXX_STANDARD 23) | ||||||
set(CMAKE_CXX_STANDARD_REQUIRED True) | ||||||
|
||||||
# Collect all .cpp files except the legacy monolithic implementation | ||||||
file(GLOB_RECURSE SRCS ${PROJECT_SOURCE_DIR}/src/**.cpp) | ||||||
# Remove serial.cpp from the list (it has been superseded by modular implementations) | ||||||
list(REMOVE_ITEM SRCS ${PROJECT_SOURCE_DIR}/src/serial.cpp) | ||||||
|
||||||
set(LIB true) | ||||||
|
||||||
configure_file(versioning/version_config.cpp.in ${PROJECT_SOURCE_DIR}/src/version_config.cpp) | ||||||
|
||||||
# a macro that gets all of the header containing directories. | ||||||
MACRO(header_directories return_list includes_base_folder extention ) | ||||||
FILE(GLOB_RECURSE new_list ${includes_base_folder}/*.${extention}) | ||||||
|
@@ -72,34 +90,27 @@ else() | |||||
add_executable(${PROJECT_N} ${SRCS}) | ||||||
endif(LIB) | ||||||
|
||||||
target_link_libraries(${PROJECT_N} PRIVATE cpp_core) | ||||||
|
||||||
set_target_properties(${PROJECT_N} PROPERTIES | ||||||
VERSION ${PROJECT_VERSION} | ||||||
SOVERSION ${PROJECT_VERSION_MAJOR}) | ||||||
|
||||||
target_include_directories(${PROJECT_N} PUBLIC ${PROJECT_SOURCE_DIR}/src) | ||||||
|
||||||
add_executable(serial_integration_tests tests/serial_test.cpp) | ||||||
target_link_libraries(serial_integration_tests PRIVATE ${PROJECT_N} gtest_main) | ||||||
|
||||||
target_include_directories(serial_integration_tests PRIVATE ${PROJECT_SOURCE_DIR}/src) | ||||||
add_executable(tests | ||||||
tests/serial_test.cpp | ||||||
tests/serial_unit_tests.cpp) | ||||||
|
||||||
add_custom_command(TARGET serial_integration_tests POST_BUILD | ||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different | ||||||
$<TARGET_FILE:${PROJECT_N}> | ||||||
$<TARGET_FILE_DIR:serial_integration_tests> | ||||||
COMMENT "Copy shared library next to test binary") | ||||||
target_link_libraries(tests PRIVATE ${PROJECT_N} gtest cpp_core) | ||||||
|
||||||
# Unit test target covering additional API aspects | ||||||
add_executable(serial_unit_tests tests/serial_unit_tests.cpp) | ||||||
target_link_libraries(serial_unit_tests PRIVATE ${PROJECT_N} gtest_main) | ||||||
target_include_directories(serial_unit_tests PRIVATE ${PROJECT_SOURCE_DIR}/src) | ||||||
target_include_directories(tests PRIVATE ${PROJECT_SOURCE_DIR}/src) | ||||||
|
||||||
add_custom_command(TARGET serial_unit_tests POST_BUILD | ||||||
add_custom_command(TARGET tests POST_BUILD | ||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different | ||||||
$<TARGET_FILE:${PROJECT_N}> | ||||||
$<TARGET_FILE_DIR:serial_unit_tests> | ||||||
COMMENT "Copy shared library next to unit test binary") | ||||||
$<TARGET_FILE_DIR:tests> | ||||||
COMMENT "Copy shared library next to aggregated test binary") | ||||||
|
||||||
enable_testing() | ||||||
add_test(NAME SerialEchoTest COMMAND serial_integration_tests /dev/ttyUSB0) | ||||||
add_test(NAME SerialUnitTests COMMAND serial_unit_tests) | ||||||
add_test(NAME AllTests COMMAND tests /dev/ttyUSB0) |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,7 +1,7 @@ | ||||||
# CPP-Unix-Bindings | ||||||
# cpp_unix_bindings | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
A compact C++23 library for talking to serial devices on Linux (e.g. Arduino). | ||||||
The project builds a **shared library `libCPP-Unix-Bindings.so`** that can be used via | ||||||
The project builds a **shared library `libcpp_unix_bindings.so`** that can be used via | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Deno's native FFI. | ||||||
|
||||||
--- | ||||||
|
@@ -21,7 +21,7 @@ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release | |||||
cmake --build build -j | ||||||
|
||||||
# The resulting library will be located at | ||||||
# build/libCPP-Unix-Bindings.so | ||||||
# build/libcpp_unix_bindings.so | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
``` | ||||||
|
||||||
> Because `CMAKE_EXPORT_COMPILE_COMMANDS` is enabled, the build also generates a | ||||||
|
@@ -36,7 +36,7 @@ Deno ships with a first-class FFI API. | |||||
|
||||||
```ts | ||||||
// serial_deno.ts | ||||||
const lib = Deno.dlopen('./build/libCPP-Unix-Bindings.so', { | ||||||
const lib = Deno.dlopen('./build/libcpp_unix_bindings.so', { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
serialOpen: { parameters: [ 'buffer', 'i32', 'i32', 'i32', 'i32' ], result: 'pointer' }, | ||||||
serialClose: { parameters: [ 'pointer' ], result: 'void' }, | ||||||
serialRead: { parameters: [ 'pointer', 'buffer', 'i32', 'i32', 'i32' ], result: 'i32' }, | ||||||
|
@@ -64,16 +64,72 @@ lib.close(); | |||||
|
||||||
## 3 C API reference | ||||||
|
||||||
| Function | Description | | ||||||
|----------|-------------| | ||||||
| `intptr_t serialOpen(const char* dev, int baud, int bits, int parity, int stop)` | Open a device and return a handle. | | ||||||
| `void serialClose(intptr_t handle)` | Close the port. | | ||||||
| `int serialRead(...)` | Read bytes with timeout. | | ||||||
| `int serialWrite(...)` | Write bytes with timeout. | | ||||||
| `int serialGetPortsInfo(char* buffer, int len, const char* sep)` | List ports under `/dev/serial/by-id`. | | ||||||
| `void serialOnError(void (*)(int))` | Register an error callback. | | ||||||
| *(others in `serial.h`)* | | ||||||
|
||||||
Return values ≤ 0 indicate error codes defined in `status_codes.h`. | ||||||
Below is a condensed overview of the most relevant functions. See `serial.h` for full | ||||||
signatures and additional helpers. | ||||||
|
||||||
### Connection | ||||||
* `serialOpen(...)` – open a serial device and return a handle | ||||||
* `serialClose(handle)` – close the device | ||||||
|
||||||
### I/O | ||||||
* `serialRead(...)` / `serialWrite(...)` – basic read/write with timeout | ||||||
* `serialReadUntil(...)` – read until a specific char is encountered (inclusive) | ||||||
* `serialReadLine(...)` – read until `\n` | ||||||
* `serialWriteLine(...)` – write buffer and append `\n` | ||||||
* `serialReadUntilToken(...)` – read until a string token is encountered | ||||||
* `serialReadFrame(...)` – read a frame delimited by start & end bytes | ||||||
|
||||||
### Helpers | ||||||
* `serialPeek(...)` – look at the next byte without consuming it | ||||||
* `serialDrain(...)` – wait until all TX bytes are sent | ||||||
* `serialClearBufferIn/Out(...)` – drop buffered bytes | ||||||
* `serialAbortRead/Write(...)` – abort pending I/O operations | ||||||
|
||||||
### Statistics | ||||||
* `serialGetRxBytes(handle)` / `serialGetTxBytes(handle)` – cumulative RX / TX byte counters | ||||||
|
||||||
### Enumeration & autodetect | ||||||
* `serialGetPortsInfo(...)` – list available ports under `/dev/serial/by-id` | ||||||
|
||||||
### Callbacks | ||||||
* `serialOnError(func)` – error callback | ||||||
* `serialOnRead(func)` – read callback (bytes read) | ||||||
* `serialOnWrite(func)` – write callback (bytes written) | ||||||
|
||||||
Return values ≤ 0 correspond to error codes defined in `status_codes.h`. | ||||||
|
||||||
--- | ||||||
|
||||||
## 4 Ready-made Deno examples | ||||||
|
||||||
Two runnable scripts live in `examples/` and require only Deno plus the compiled | ||||||
shared library. | ||||||
|
||||||
- **serial_echo.ts** – Minimal echo test that lists available ports, opens the | ||||||
first one and verifies that the micro-controller echoes the sent string. | ||||||
Run it with: | ||||||
```bash | ||||||
deno run --allow-ffi --allow-read examples/serial_echo.ts \ | ||||||
--lib ./build/libcpp_unix_bindings.so \ | ||||||
--port /dev/ttyUSB0 | ||||||
``` | ||||||
|
||||||
- **serial_advanced.ts** – Shows the high-level helpers (`serialWriteLine`, | ||||||
`serialReadLine`, `serialPeek`, `statistics`, `serialDrain`). It sends three | ||||||
lines and then prints the TX/RX counters. | ||||||
```bash | ||||||
deno run --allow-ffi --allow-read examples/serial_advanced.ts \ | ||||||
--lib ./build/libcpp_unix_bindings.so \ | ||||||
--port /dev/ttyUSB0 | ||||||
``` | ||||||
|
||||||
Notes: | ||||||
1. `--lib` defaults to `./build/libcpp_unix_bindings.so`; pass a custom path if | ||||||
you installed the library elsewhere. | ||||||
2. `--port` defaults to `/dev/ttyUSB0`; adjust if your board shows up under a | ||||||
different device (e.g. `/dev/ttyACM0`). | ||||||
3. On most Arduino boards opening the port toggles DTR and triggers a reset. | ||||||
Both scripts therefore wait ~2 s after opening the device before sending the | ||||||
first command. | ||||||
|
||||||
--- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# C API Reference | ||
|
||
**Scope** – Public interface of `libcpp_unix_bindings` (C-linkable). | ||
*Linux fully supported · Windows fully supported* | ||
|
||
Every function is declared in `src/serial.h` and follows the same | ||
convention: **positive / non-zero** ⇒ success, **≤ 0** ⇒ failure (maps to | ||
`StatusCodes`). | ||
|
||
--- | ||
|
||
## 1 · Connection | ||
|
||
```c | ||
intptr_t serialOpen(void* port, int baud, int dataBits, | ||
int parity /*0-none·1-even·2-odd*/, | ||
int stopBits /*0-1bit·2-2bit*/); | ||
|
||
void serialClose(int64_t handle); | ||
``` | ||
|
||
*Linux*: `port` is a NULL-terminated path like `/dev/ttyUSB0` or | ||
`/dev/tty.SLAB_USBtoUART`. | ||
*Windows*: use `"COM3"`, `"\\.\\COM12"`, etc. (to be implemented). | ||
|
||
--- | ||
|
||
## 2 · Basic I/O | ||
|
||
```c | ||
int serialRead (int64_t handle, void* buf, int len, int timeoutMs, int mult); | ||
int serialWrite(int64_t handle, const void* buf, int len, int timeoutMs, int mult); | ||
|
||
int serialReadUntil(int64_t handle, void* buf, int len, | ||
int timeoutMs, int mult, void* untilCharPtr); | ||
``` | ||
|
||
*mult* is a polling multiplier (usually **1**). `serialReadUntil` stops **after** | ||
the delimiter byte has been copied into *buf*. | ||
|
||
--- | ||
|
||
## 3 · Convenience helpers | ||
|
||
```c | ||
int serialReadLine (int64_t h, void* buf, int len, int timeoutMs); // until '\n' | ||
int serialWriteLine(int64_t h, const void* buf, int len, int timeoutMs); // appends '\n' | ||
|
||
int serialReadUntilSequence(int64_t h, void* buf, int len, | ||
int timeoutMs, void* sequencePtr); | ||
|
||
int serialReadFrame(int64_t h, void* buf, int len, int timeoutMs, | ||
char startByte, char endByte); | ||
``` | ||
|
||
--- | ||
|
||
## 4 · Statistics & Buffer control | ||
|
||
```c | ||
int64_t serialInBytesTotal (int64_t handle); // cumulative RX | ||
int64_t serialOutBytesTotal(int64_t handle); // cumulative TX | ||
|
||
int serialInBytesWaiting (int64_t handle); // queued in driver | ||
int serialOutBytesWaiting(int64_t handle); | ||
|
||
int serialDrain(int64_t handle); // wait for TX empty | ||
|
||
void serialClearBufferIn (int64_t handle); | ||
void serialClearBufferOut(int64_t handle); | ||
|
||
void serialAbortRead (int64_t handle); | ||
void serialAbortWrite(int64_t handle); | ||
``` | ||
|
||
--- | ||
|
||
## 5 · Port enumeration & Callbacks | ||
|
||
```c | ||
// Enumerate available ports. For each port the callback receives: | ||
// portPath, aliasPath, manufacturer, serialNumber, pnpId, | ||
// locationId, productId, vendorId | ||
int serialGetPortsInfo(void (*cb)(const char*, const char*, const char*, | ||
const char*, const char*, const char*, | ||
const char*, const char*)); | ||
|
||
void serialOnError(void (*cb)(int code, const char* msg)); | ||
void serialOnRead (void (*cb)(int bytes)); | ||
void serialOnWrite(void (*cb)(int bytes)); | ||
``` | ||
|
||
*Linux*: implementation scans `/dev/serial/by-id` if present, otherwise falls | ||
back to common tty names. | ||
*Windows*: will iterate over available COM ports. | ||
|
||
--- | ||
|
||
## 6 · StatusCodes | ||
|
||
```c++ | ||
enum class StatusCodes : int { | ||
SUCCESS = 0, | ||
CLOSE_HANDLE_ERROR = -1, | ||
INVALID_HANDLE_ERROR = -2, | ||
READ_ERROR = -3, | ||
WRITE_ERROR = -4, | ||
GET_STATE_ERROR = -5, | ||
SET_STATE_ERROR = -6, | ||
SET_TIMEOUT_ERROR = -7, | ||
BUFFER_ERROR = -8, | ||
NOT_FOUND_ERROR = -9, | ||
CLEAR_BUFFER_IN_ERROR = -10, | ||
CLEAR_BUFFER_OUT_ERROR = -11, | ||
ABORT_READ_ERROR = -12, | ||
ABORT_WRITE_ERROR = -13 | ||
}; | ||
``` | ||
|
||
--- | ||
|
||
### Error-handling idiom | ||
|
||
```c | ||
int rv = serialWrite(h, data, len, 500, 1); | ||
if (rv <= 0) { | ||
// handle error – detailed info via registered onError callback (if any) | ||
} | ||
``` |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.