524 lines
20 KiB
Markdown
524 lines
20 KiB
Markdown
# Byonoy Device Library — Native SDK
|
|
|
|
Read the **[SDK guide](README.md)** 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 public native SDK is a free download.** Each release on the
|
|
[releases page](https://git.byonoy.com/public/byonoy_devices_sdk/releases) carries it as `byonoy-devices-public-sdk-<version>.zip`,
|
|
next to a `SHA256SUMS` file. No account or token is needed:
|
|
|
|
```bash
|
|
TAG=v2026.09.1
|
|
curl -fsSLO "https://git.byonoy.com/public/byonoy_devices_sdk/releases/download/${TAG}/byonoy-devices-public-sdk-${TAG}.zip"
|
|
curl -fsSLO "https://git.byonoy.com/public/byonoy_devices_sdk/releases/download/${TAG}/SHA256SUMS"
|
|
sha256sum --ignore-missing -c SHA256SUMS # macOS: shasum -a 256 --ignore-missing -c SHA256SUMS
|
|
unzip -q "byonoy-devices-public-sdk-${TAG}.zip" -d sdk
|
|
```
|
|
|
|
In Windows PowerShell — `curl.exe`, not `curl`, which is an alias for
|
|
`Invoke-WebRequest` there:
|
|
|
|
```powershell
|
|
$TAG = (Invoke-RestMethod https://git.byonoy.com/api/v1/repos/public/byonoy_devices_sdk/releases/latest).tag_name
|
|
$base = "https://git.byonoy.com/public/byonoy_devices_sdk/releases/download/$TAG"
|
|
$zip = "byonoy-devices-public-sdk-$TAG.zip"
|
|
curl.exe -fsSLO "$base/$zip"
|
|
curl.exe -fsSLO "$base/SHA256SUMS"
|
|
$expected = ((Select-String -Path SHA256SUMS -SimpleMatch $zip).Line -split '\s+')[0]
|
|
if ((Get-FileHash $zip -Algorithm SHA256).Hash -ne $expected.ToUpper()) { throw "checksum mismatch" }
|
|
Expand-Archive $zip -DestinationPath sdk
|
|
```
|
|
|
|
**The internal native SDK is available on request**, as
|
|
`byonoy-devices-internal-sdk-<version>.zip`. If you have been given access to
|
|
the internal repository's releases, it is attached to each release there and
|
|
can be downloaded with your token:
|
|
|
|
```bash
|
|
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-internal-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`). 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/bin/libhidapi.dll, libgcc_s_seh-1.dll,
|
|
libstdc++-6.dll, libwinpthread-1.dll Windows dependencies, ship alongside
|
|
sdk/lib/libbyonoy_device_library.dll.a Windows import library (GNU format)
|
|
sdk/lib/libhidapi* Linux/macOS dependency, ships alongside
|
|
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 and C++ examples. `bin/` holds the Windows runtime only — on macOS and
|
|
Linux everything you need is in `lib/`.
|
|
|
|
---
|
|
|
|
## 3. Build and run
|
|
|
|
### Prerequisites
|
|
|
|
A C compiler, CMake for the bundled examples, and `unzip`:
|
|
|
|
| Platform | Install |
|
|
|---|---|
|
|
| Debian, Ubuntu | `sudo apt update && sudo apt install -y build-essential cmake unzip` |
|
|
| Fedora | `sudo dnf install gcc make cmake unzip` |
|
|
| macOS | `xcode-select --install`, then `brew install cmake` |
|
|
| Windows | a MinGW-w64 toolchain — see below |
|
|
|
|
On **Windows**, the library is a **MinGW-w64 build** (GCC, x86_64, SEH
|
|
exceptions, POSIX threads, msvcrt), and its import library
|
|
`libbyonoy_device_library.dll.a` is in GNU format. Use a matching MinGW-w64 GCC;
|
|
WinLibs provides one, with Ninja included:
|
|
|
|
```powershell
|
|
winget install BrechtSanders.WinLibs.POSIX.MSVCRT
|
|
winget install Kitware.CMake
|
|
```
|
|
|
|
Open a new shell afterwards so both are on `PATH`. Visual Studio (MSVC) cannot
|
|
use the shipped import library as it is.
|
|
|
|
### Finding the library at run time
|
|
|
|
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
|
|
Referenced from: … demo
|
|
Reason: no LC_RPATH's found
|
|
```
|
|
|
|
Give the executable an RPATH, relative to itself so the result stays portable:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
DYLD_LIBRARY_PATH=sdk/lib ./demo # macOS
|
|
LD_LIBRARY_PATH=sdk/lib ./demo # Linux
|
|
```
|
|
|
|
On **Windows** there is no RPATH. Compile, then **copy all five DLLs from
|
|
`sdk\bin` next to the executable** — they travel with it:
|
|
|
|
```powershell
|
|
gcc -I sdk/include main.c -L sdk/lib -lbyonoy_device_library -o demo.exe
|
|
Copy-Item sdk\bin\*.dll .
|
|
.\demo.exe
|
|
```
|
|
|
|
A missing DLL produces no message: the program just exits with code
|
|
`0xC0000135` (`-1073741515`). Windows also looks in the current directory, so a
|
|
test run from inside the SDK folder can hide a DLL you forgot to copy. Do not
|
|
rely on adding `sdk\bin` to `PATH`: when a MinGW toolchain is on `PATH` too,
|
|
Windows may load the toolchain's own `libstdc++-6.dll` and `libgcc_s_seh-1.dll`
|
|
instead of the ones shipped with the SDK.
|
|
|
|
---
|
|
|
|
## 4. Smallest complete program
|
|
|
|
```c
|
|
#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"); /* nothing to free */
|
|
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`/`free` pair**. Call
|
|
`create` first, `free` exactly once, and never free something you did not
|
|
create.
|
|
- Functions returning `int`, `float` or `bool` through a pointer allocate
|
|
nothing.
|
|
- `byonoy_available_devices()` hands out a list owned by the library; release
|
|
it with `byonoy_free_available_devices()` once you have opened what you need
|
|
(an empty list needs no release, though releasing it is harmless). The
|
|
`byonoy_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 return `BYONOY_ERROR_INVALID_ARGUMENT` (`0x0003`) —
|
|
*not* `DEVICE_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 luminescence program, so it can be compiled as it stands:
|
|
|
|
```c
|
|
#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"); 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](README.md#well-order) (`value[0]` is A1, `value[12]` is B1), as
|
|
is `selected_wells`. The capability table in the
|
|
[Python document](SDK_PYTHON.md#which-modality-does-this-device-support) is also
|
|
the C capability table, with `byonoy_` prefixes.
|
|
|
|
### Absorbance
|
|
|
|
An Absorbance 96 Automate needs more steps: check the device's health, query the
|
|
wavelengths (required before a single-wavelength initialise), initialise with
|
|
the slot **empty**, wait for the plate, then measure. The library checks
|
|
neither the slot nor the plate, so the program does. Like everything else
|
|
here, the status, slot and wavelength objects come in `create`/`free` pairs:
|
|
|
|
```c
|
|
#define _POSIX_C_SOURCE 200809L /* nanosleep */
|
|
#include "byonoy_device_library.h"
|
|
#include <stdio.h>
|
|
#if defined(_WIN32)
|
|
#include <windows.h>
|
|
static void sleep_ms(unsigned ms) { Sleep(ms); }
|
|
#else
|
|
#include <time.h>
|
|
static void sleep_ms(unsigned ms) {
|
|
struct timespec t = {ms / 1000, (long)(ms % 1000) * 1000000L};
|
|
nanosleep(&t, NULL);
|
|
}
|
|
#endif
|
|
|
|
/* Wait until the slot reads `wanted`, for at most `seconds`. */
|
|
static int wait_for_slot(byonoy_device_handle_t handle, byonoy_device_slot_status_t wanted, int seconds) {
|
|
byonoy_device_slot_status_t* slot = NULL;
|
|
if (byonoy_create_device_slot_status(&slot) != BYONOY_ERROR_NO_ERROR) return 0;
|
|
int reached = 0;
|
|
for (int i = 0; i < seconds * 2 && !reached; ++i) {
|
|
if (byonoy_get_device_slot_status(handle, slot) == BYONOY_ERROR_NO_ERROR && *slot == wanted) reached = 1;
|
|
else sleep_ms(500);
|
|
}
|
|
byonoy_free_device_slot_status(slot);
|
|
return reached;
|
|
}
|
|
|
|
int main(void) {
|
|
byonoy_device_t* devices = NULL;
|
|
uint32_t count = 0;
|
|
byonoy_available_devices(&devices, &count);
|
|
if (count == 0) { printf("no device\n"); 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; }
|
|
|
|
byonoy_abs96_wavelengths_t* wavelengths = NULL;
|
|
byonoy_abs96_single_measurement_config_t* config = NULL;
|
|
byonoy_abs96_single_measurement_result_t* result = NULL;
|
|
byonoy_device_status_t* status = NULL;
|
|
uint32_t device_error = 0;
|
|
rc = BYONOY_ERROR_UNKNOWN_ERROR;
|
|
|
|
if (!byonoy_abs96_measurement_supported(handle)) {
|
|
printf("this device does not do 96-well absorbance\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Measure only a healthy device. */
|
|
if (byonoy_create_device_status(&status) != BYONOY_ERROR_NO_ERROR ||
|
|
byonoy_get_device_status(handle, status) != BYONOY_ERROR_NO_ERROR ||
|
|
byonoy_get_device_error(handle, &device_error) != BYONOY_ERROR_NO_ERROR) {
|
|
printf("status query failed\n");
|
|
goto done;
|
|
}
|
|
if (*status != BYONOY_DEVICE_STATE_OK || device_error != 0) {
|
|
printf("device status %d, device error 0x%x: not measuring\n", (int)*status, device_error);
|
|
goto done;
|
|
}
|
|
|
|
/* Required before a single-wavelength initialise; also says which are valid. */
|
|
if (byonoy_create_abs96_wavelengths(&wavelengths) != BYONOY_ERROR_NO_ERROR ||
|
|
byonoy_abs96_get_available_wavelengths(handle, wavelengths) != BYONOY_ERROR_NO_ERROR ||
|
|
wavelengths->wavelength_count == 0) {
|
|
printf("wavelength query failed\n");
|
|
goto done;
|
|
}
|
|
|
|
if (byonoy_create_abs96_single_measurement_config(&config) != BYONOY_ERROR_NO_ERROR ||
|
|
byonoy_create_abs96_single_measurement_result(&result) != BYONOY_ERROR_NO_ERROR) {
|
|
printf("allocation failed\n");
|
|
goto done;
|
|
}
|
|
config->sample_wavelength = wavelengths->wavelengths[0];
|
|
config->reference_wavelength = 0; /* 0 = no reference wavelength */
|
|
config->rapid_mode = false;
|
|
|
|
printf("remove any plate...\n"); fflush(stdout);
|
|
if (!wait_for_slot(handle, BYONOY_SLOT_EMPTY, 120)) { printf("slot not empty\n"); goto done; }
|
|
rc = byonoy_abs96_initialize_single_measurement(handle, config);
|
|
if (rc != BYONOY_ERROR_NO_ERROR) { printf("initialise failed: 0x%04x\n", rc); goto done; }
|
|
|
|
printf("insert the plate...\n"); fflush(stdout);
|
|
if (!wait_for_slot(handle, BYONOY_SLOT_OCCUPIED, 120)) {
|
|
printf("no plate inserted\n");
|
|
rc = BYONOY_ERROR_UNKNOWN_ERROR;
|
|
goto done;
|
|
}
|
|
sleep_ms(1000); /* let the plate settle */
|
|
|
|
rc = byonoy_abs96_single_measure(handle, config, result);
|
|
if (rc == BYONOY_ERROR_NO_ERROR) {
|
|
printf("OD at %u nm:\n", (unsigned)config->sample_wavelength);
|
|
for (int i = 0; i < 96; ++i) {
|
|
printf("%7.3f", result->value[i]);
|
|
if ((i + 1) % 12 == 0) printf("\n");
|
|
}
|
|
} else {
|
|
printf("measurement failed: 0x%04x\n", rc);
|
|
}
|
|
|
|
done:
|
|
if (result) byonoy_free_abs96_single_measurement_result(result);
|
|
if (config) byonoy_free_abs96_single_measurement_config(config);
|
|
if (wavelengths) byonoy_free_abs96_wavelengths(wavelengths);
|
|
if (status) byonoy_free_device_status(status);
|
|
byonoy_free_device(handle);
|
|
return rc == BYONOY_ERROR_NO_ERROR ? 0 : 1;
|
|
}
|
|
```
|
|
|
|
The rules behind it — wavelengths per unit, initialisation per handle and per
|
|
wavelength, the plate check — are in the
|
|
[Python document's absorbance section](SDK_PYTHON.md#absorbance); they apply to
|
|
C unchanged. An Absorbance One works the same way with
|
|
`byonoy_absone_initialize_measurement` and `byonoy_absone_measure`, but cannot
|
|
sense its slot; check `byonoy_absone_is_initialized` before measuring, because
|
|
on a faulty device the measure can block indefinitely.
|
|
|
|
### Bundled examples
|
|
|
|
`sdk/examples/C` holds compilable references, one directory each with a
|
|
`main.c` and a `CMakeLists.txt`:
|
|
|
|
| Path | Executable | Shows |
|
|
|---|---|---|
|
|
| `examples/C/device-info` | `device-information` | open, read information, close |
|
|
| `examples/C/lum96-measurement` | `lum96-measurement` | 96-well luminescence |
|
|
| `examples/C/abs96-multi-measurement` | `abs96-multi-wavelength` | multi-wavelength absorbance |
|
|
| `examples/C/absone-measurement` | `absone-measurement` | single-cuvette absorbance |
|
|
| `examples/C/device-update` | `device-update` | firmware update — **read before running**, see below |
|
|
|
|
They are references more than finished programs: they wait a fixed time for a
|
|
plate instead of checking the slot, and print return codes in decimal. Build
|
|
one with CMake:
|
|
|
|
```bash
|
|
cmake -S sdk/examples/C/device-info -B build-example
|
|
cmake --build build-example
|
|
./build-example/device-information
|
|
```
|
|
|
|
On Windows, add a generator — CMake otherwise looks for Visual Studio's
|
|
`nmake` — and copy the DLLs next to the result. Delete the build directory
|
|
after a failed configure; CMake caches the failed choice:
|
|
|
|
```powershell
|
|
cmake -S sdk/examples/C/device-info -B build-example -G Ninja
|
|
cmake --build build-example
|
|
Copy-Item sdk\bin\*.dll build-example\
|
|
.\build-example\device-information.exe
|
|
```
|
|
|
|
Known problems in the v2026.09.1 examples:
|
|
|
|
- **The binaries only run from the build directory.** CMake gives them an
|
|
absolute RPATH into the SDK, so they stop working when either is moved, and
|
|
`cmake --install` strips it entirely — the installed copy cannot find the
|
|
library. The default install location is the SDK folder itself. For a
|
|
binary you keep, use the relative RPATH from the `cc` lines above.
|
|
- **`absone-measurement` does not configure**: its `CMakeLists.txt` installs a
|
|
target named `abs96-multi-wavelength`. Change that line to
|
|
`install(TARGETS absone-measurement …)`.
|
|
- **`device-update` flashes a fixed file**, `./abs96auto-update.byoup`, onto
|
|
the first device it finds that supports updates — whatever its type. Adapt
|
|
it before running it.
|
|
|
|
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:
|
|
|
|
```bash
|
|
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](README.md#1-which-variant-public-or-internal)
|
|
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](SDK_PYTHON.md#what-it-adds); 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.
|