11 KiB
Byonoy Device Library — Native SDK
Read the SDK guide first: it explains the two variants, how the library behaves, and the error codes. This document is the C/C++ specifics.
Everything up to section 6 is the public variant. Section 6 covers what the internal variant adds.
1. Getting the SDK
The native SDK is available on request, in either variant. It is not
published anywhere you can fetch it from without Byonoy providing it. Ask for
the variant you need and you will be given an archive,
byonoy-devices-public-sdk-<version>.zip or
byonoy-devices-internal-sdk-<version>.zip.
If you have been given access to the internal repository's releases, the archive is attached to each release and can be downloaded with your token:
TAG=$(curl -fsS -H "Authorization: token $BYONOY_TOKEN" \
'https://git.byonoy.com/api/v1/repos/sw/byonoy_device_library/releases?limit=1' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)[0]["tag_name"])')
curl -fsSL -H "Authorization: token $BYONOY_TOKEN" -o sdk.zip \
"https://git.byonoy.com/sw/byonoy_device_library/releases/download/${TAG}/byonoy-devices-public-sdk-${TAG}.zip"
unzip -q sdk.zip -d sdk
Use that releases/download/... URL; the /api/v1/.../releases/assets/<id>
endpoint returns 404 on this instance.
limit=1 takes the newest release, which may be a pre-release (a tag with a
-suffix). Public archives are attached to final tags; a recent pre-release tag
carries internal artifacts only. Check what a release actually has rather than
assuming.
2. Layout
One archive carries every platform the release was built for; take the files for yours, and check yours is present before building.
sdk/include/byonoy_device_library.h the API
sdk/lib/libbyonoy_device_library.so Linux
sdk/lib/libbyonoy_device_library.dylib macOS
sdk/bin/libbyonoy_device_library.dll Windows runtime
sdk/lib/libbyonoy_device_library.dll.a Windows import library
sdk/lib/libhidapi* dependency, ships alongside
sdk/examples/C, sdk/examples/C++ compilable references
sdk/third-party-licenses/ notices for bundled dependencies
The internal archive is the same with _internal appended to every library
name, plus a second header. bin/ holds the Windows runtime only — on macOS and
Linux everything you need is in lib/.
3. Build and run
Compiling is not enough: you must also tell your binary where to find the
library at run time. The shipped library's install name is
@rpath/libbyonoy_device_library.dylib, and its own RPATH entries only cover how
it finds hidapi — they do nothing for your executable. Link without an RPATH of
your own and you get a clean compile followed by:
dyld: Library not loaded: @rpath/libbyonoy_device_library.dylib
Reason: no LC_RPATH's found
Give the executable an RPATH, relative to itself so the result stays portable:
# macOS
cc -I sdk/include main.c -L sdk/lib -lbyonoy_device_library \
-Wl,-rpath,@executable_path/sdk/lib -o demo
# Linux
cc -I sdk/include main.c -L sdk/lib -lbyonoy_device_library \
-Wl,-rpath,'$ORIGIN/sdk/lib' -o demo
Adjust the RPATH to wherever lib/ sits relative to the finished binary. As a
throwaway alternative you can set the loader path in the environment, but this
does not travel with the binary:
DYLD_LIBRARY_PATH=sdk/lib ./demo # macOS
LD_LIBRARY_PATH=sdk/lib ./demo # Linux
On Windows, put sdk/bin on PATH; the DLLs there must travel with the
executable.
The examples below use uint32_t and true, which the SDK header provides on a
current compiler. Add #include <stdint.h> and <stdbool.h> if yours is older
than C23.
4. Smallest complete program
#include "byonoy_device_library.h"
#include <stdio.h>
int main(void) {
byonoy_device_t* devices = NULL;
uint32_t count = 0;
byonoy_available_devices(&devices, &count); /* returns void */
if (count == 0) {
printf("no device\n");
byonoy_free_available_devices(); /* allocated even when empty */
return 1;
}
byonoy_device_handle_t handle;
byonoy_error_code rc = byonoy_open_device(devices, &handle);
byonoy_free_available_devices(); /* list is dead once opened */
if (rc != BYONOY_ERROR_NO_ERROR) {
printf("open failed: 0x%04x\n", rc);
return 1;
}
byonoy_device_info_t* info = NULL;
rc = byonoy_create_device_information(&info);
if (rc != BYONOY_ERROR_NO_ERROR) { /* create/free pair */
printf("allocation failed: 0x%04x\n", rc);
byonoy_free_device(handle);
return 1;
}
rc = byonoy_get_device_information(handle, info);
if (rc == BYONOY_ERROR_NO_ERROR) {
printf("%s %s %s\n", info->ref_no, info->sn, info->version);
} else {
printf("device info failed: 0x%04x\n", rc);
}
byonoy_free_device_information(info);
byonoy_free_device(handle);
return rc == BYONOY_ERROR_NO_ERROR ? 0 : 1;
}
byonoy_device_info_t carries sn, ref_no and version as const char*,
and type as a byonoy_device_types enum.
Memory rules
- Anything returned through a pointer has a
create/freepair. Callcreatefirst,freeexactly once, and never free something you did not create. - Functions returning
int,floatorboolthrough a pointer allocate nothing. byonoy_available_devices()allocates a list; release it withbyonoy_free_available_devices()once you have opened what you need. Thebyonoy_device_t*entries are invalid afterwards — the handle is what stays valid.byonoy_free_device(handle)closes the device. The handle is invalid after that, and calls with it returnBYONOY_ERROR_INVALID_ARGUMENT(0x0003) — notDEVICE_CLOSED, which is what a disconnected but still-open device gives you.
5. Taking a measurement
Gate on the predicate, create the config and result, measure, free both. A
complete program, so it can be compiled as it stands:
#include "byonoy_device_library.h"
#include <stdio.h>
int main(void) {
byonoy_device_t* devices = NULL;
uint32_t count = 0;
byonoy_available_devices(&devices, &count);
if (count == 0) { printf("no device\n"); byonoy_free_available_devices(); return 1; }
byonoy_device_handle_t handle;
byonoy_error_code rc = byonoy_open_device(devices, &handle);
byonoy_free_available_devices();
if (rc != BYONOY_ERROR_NO_ERROR) { printf("open failed: 0x%04x\n", rc); return 1; }
if (!byonoy_lum96_measurement_supported(handle)) {
printf("this device does not do 96-well luminescence\n");
byonoy_free_device(handle);
return 1;
}
byonoy_lum96_measurement_config_t* config = NULL;
byonoy_lum96_measurement_result_t* result = NULL;
if (byonoy_create_lum96_measurement_config(&config) != BYONOY_ERROR_NO_ERROR ||
byonoy_create_lum96_measurement_result(&result) != BYONOY_ERROR_NO_ERROR) {
printf("allocation failed\n");
byonoy_free_device(handle);
return 1;
}
config->mode = BYONOY_LUM96_INTEGRATION_RAPID; /* ~10 s; SENSITIVE is ~60 s */
for (int i = 0; i < 96; ++i) config->selected_wells[i] = true;
rc = byonoy_lum96_measure(handle, config, result);
if (rc == BYONOY_ERROR_NO_ERROR) {
for (int i = 0; i < 96; ++i) {
printf("%8.1f", result->value[i]);
if ((i + 1) % 12 == 0) printf("\n");
}
} else {
printf("measurement failed: 0x%04x\n", rc);
}
byonoy_free_lum96_measurement_result(result);
byonoy_free_lum96_measurement_config(config);
byonoy_free_device(handle);
return rc == BYONOY_ERROR_NO_ERROR ? 0 : 1;
}
Set both mode and selected_wells explicitly. A freshly created config
has every well deselected, and measuring with it succeeds — returning
instantly with 96 zeros and no error. Its default mode is
BYONOY_LUM96_INTEGRATION_RAPID (0), which differs from the Python binding's
default; do not rely on either.
The mode enum is
BYONOY_LUM96_INTEGRATION_{RAPID,SENSITIVE,ULTRA_SENSITIVE,CUSTOM}, and
result->value is a fixed float[96] in row-major
well order (value[0] is A1, value[12] is B1), as
is selected_wells. The capability table in the
Python document is also
the C capability table, with byonoy_ prefixes.
Bundled examples
sdk/examples/ holds compilable references, one directory each with a main.c
or main.cpp and a CMakeLists.txt:
| Path | Shows |
|---|---|
examples/C/device-info |
open, read information, close |
examples/C/lum96-measurement |
96-well luminescence |
examples/C/abs96-multi-measurement |
multi-wavelength absorbance |
examples/C/absone-measurement |
single-cuvette absorbance |
examples/C/device-update |
firmware update |
examples/C++/async |
asynchronous measurement (internal variant) |
examples/C++/rpc |
device RPC (internal variant) |
They build with CMake, and CMake sets the RPATH for you — which makes this the
safer route than the cc line above:
cmake -S sdk/examples/C/device-info -B build-example
cmake --build build-example
./build-example/device-information
Against the internal archive their find_library call fails, because they
look for the public library name while the internal archive ships
libbyonoy_device_library_internal.*. Point the cache variable at the real file:
cmake -S sdk/examples/C/device-info -B build-example \
-DBYONOY_DEVICE_LIBRARY="$PWD/sdk/lib/libbyonoy_device_library_internal.dylib"
6. The internal variant
Available on request; see the SDK guide for when you should want it.
The C API is identical. Everything above applies unchanged except the
library name — link -lbyonoy_device_library_internal instead. The extra
functionality arrives as a second header:
sdk/include/byonoy_device_library.h same as public
sdk/include/byonoy_device_library_internal.h the additions
byonoy_device_library_internal.h is C++, not C. It declares
namespace byonoy::device::library::internal and uses std::vector,
std::optional, std::filesystem and std::chrono in its signatures, so a
translation unit including it must be compiled as C++. The public header remains
usable from C either way — the internal header wraps its include in
extern "C".
It adds its own error enum, byonoy_internal_error_code, based at 0x10000
(NOT_ENUMERATED, UNKNOWN_NAME, UNKNOWN_ID, REQUEST_FAILED, WRONG_TYPE,
READ_ONLY, WRITE_ONLY, FILE_READ_FAILED, FILE_WRITE_FAILED,
ASYNC_OPERATION_ALREADY_RUNNING, …). These are distinct from the public codes
in the SDK guide's table, not additions to them.
The feature areas — asynchronous measurements, data fields, files, RPC,
diagnostics, LEDs, bootloader and flashing, reboot — are listed with their entry
points in the Python document; the C++ names match
apart from the namespace. Each is gated on its own *_supported predicate.
sdk/examples/C++/async and sdk/examples/C++/rpc are worked examples of two
of these, and are the best starting point for the C++ header.