C SDK
libadamo exposes the Adamo SDK through a small C ABI (adamo.h). A header-only C++17 RAII wrapper (adamo.hpp) ships alongside.
Install
Section titled “Install”The C SDK ships as a prebuilt tarball from install.adamohq.com/sdk/. Pick the build that matches your host OS, architecture, and whether you need video support, then extract it into a CMake-discoverable prefix (e.g. /opt).
# Linux arm64 (Jetson / Ubuntu 22.04 aarch64), with video:curl -fsSL -o adamo-sdk.tar.gz \ https://install.adamohq.com/sdk/adamo-sdk-linux-arm64-video-latest.tar.gz
# Verify the checksum (recommended)curl -fsSL https://install.adamohq.com/sdk/adamo-sdk-linux-arm64-video-latest.tar.gz.sha256 | shasum -a 256 -c
# Extract — the tarball expands to a top-level directorysudo tar xzf adamo-sdk.tar.gz -C /optAvailable builds:
| Tarball stem | Host |
|---|---|
adamo-sdk-linux-arm64-video | Jetson / Ubuntu 22.04 aarch64, with video pipeline |
adamo-sdk-linux-arm64 | Jetson / Ubuntu 22.04 aarch64, pub/sub only |
adamo-sdk-linux-x86_64-video | Ubuntu 22.04 x86_64, with video pipeline |
adamo-sdk-linux-x86_64 | Ubuntu 22.04 x86_64, pub/sub only |
adamo-sdk-macos-arm64 | macOS arm64, pub/sub only |
adamo-sdk-macos-x86_64 | macOS x86_64, pub/sub only |
Replace latest with a pinned version (e.g. 0.3.1) in production.
Then in your project’s CMakeLists.txt:
list(APPEND CMAKE_PREFIX_PATH "/opt/adamo-sdk-linux-arm64-video-latest")find_package(Adamo REQUIRED)target_link_libraries(my_robot PRIVATE Adamo::adamo)# C++17 wrapper:# target_link_libraries(my_robot PRIVATE Adamo::adamo_cpp)Each tarball ships:
lib/libadamo.{so,dylib}— the shared libraryinclude/adamo/adamo.h— the C headerinclude/adamo/adamo.hpp— the C++17 RAII wrapper (whenADAMO_BUILD_CPP=ONwas set, which is the default)lib/cmake/Adamo/— CMake package config sofind_package(Adamo)works
Conventions
Section titled “Conventions”- Every public symbol is prefixed
adamo_. - Functions returning a handle (
adamo_session_t *, etc.) returnNULLon failure. - Functions returning
int32_tuse0for success and-1for failure. - After any failure, call
adamo_last_error()to retrieve a thread-local error string. Valid until the next fallible call on the same thread. - Every handle returned by the library is heap-allocated and must be freed with its matching
adamo_*_freefunction. - All
_freefunctions are NULL-safe. - The library manages its own internal runtime; C callers don’t see it.
adamo_session_t *is safe to share between threads.
Errors
Section titled “Errors”const char *adamo_last_error(void);Returns the most recent error message for the current thread, or NULL if there is none. The pointer is valid until the next fallible call. Each thread has its own slot.
Protocol
Section titled “Protocol”typedef enum adamo_protocol_t { adamo_protocol_t_ADAMO_PROTOCOL_UDP = 0, adamo_protocol_t_ADAMO_PROTOCOL_QUIC = 1, adamo_protocol_t_ADAMO_PROTOCOL_TCP = 2,} adamo_protocol_t;QUIC is the recommended default — reliable streams over a single multiplexed UDP socket. UDP is the lowest-latency unreliable transport. TCP is a fallback for environments that block UDP/QUIC.
Sessions
Section titled “Sessions”adamo_session_t *adamo_open_default(const char *api_key);adamo_session_t *adamo_open(const char *api_key, adamo_protocol_t protocol);void adamo_session_free(adamo_session_t *sess);
const char *adamo_session_org(const adamo_session_t *sess); // NUL-terminatedsize_t adamo_session_org_len(const adamo_session_t *sess); // bytes, excluding NULadamo_open_default opens with the default transport. adamo_open is available when you need to select a specific transport. Both return NULL on failure. adamo_session_free is NULL-safe and closes the underlying connection if this was the last reference.
adamo_session_org returns a NUL-terminated C string valid for the lifetime of the session. adamo_session_org_len is provided for callers that prefer length-prefixed reads.
Sample
Section titled “Sample”typedef struct adamo_sample_t { char *key; // NUL-terminated, prefix-stripped uint8_t *payload; // owned by the struct uintptr_t payload_len; int32_t is_delete; // 0 = PUT, 1 = DELETE (liveliness-gone)} adamo_sample_t;
void adamo_sample_free(adamo_sample_t *sample); // NULL-safeSamples returned from adamo_sub_recv / adamo_sub_try_recv / adamo_get / callback subscribers are heap-allocated and must be freed.
Publish
Section titled “Publish”One-shot put
Section titled “One-shot put”int32_t adamo_put( const adamo_session_t *sess, const char *key, const uint8_t *payload, uintptr_t payload_len, uint8_t priority, // 0–255, mapped to 8 priority classes; ≥240 = REAL_TIME int32_t express // 1 = bypass batching for lower latency);Returns 0 on success, -1 on failure.
Persistent publisher
Section titled “Persistent publisher”adamo_publisher_t *adamo_publisher( const adamo_session_t *sess, const char *key, uint8_t priority, int32_t express, int32_t reliable // 1 = RELIABLE, 0 = BEST_EFFORT);
int32_t adamo_publisher_put( const adamo_publisher_t *pub, const uint8_t *payload, uintptr_t payload_len);
void adamo_publisher_free(adamo_publisher_t *pub);adamo_publisher_free undeclares before freeing. NULL-safe.
int32_t adamo_log( const adamo_session_t *sess, const char *name, // robot name const char *message, const char *level // e.g. "info", "warn", "error", "debug" (may be NULL));Publish a log line from the given robot. The web operator console subscribes to this stream and renders entries in real time.
level is a free-form string; the standard values "info", "warn", "error", "debug" are rendered with colour. Passing NULL for level defaults to "info". Messages are truncated at 10,000 characters. Each entry is stamped with the fabric clock so lines from multiple robots stay ordered in the operator view.
Returns 0 on success, -1 on failure.
adamo_log(sess, "my-robot", "booted and attached camera", "info");adamo_log(sess, "my-robot", "encoder dropped a frame", "warn");Subscribe — pull
Section titled “Subscribe — pull”adamo_subscriber_t *adamo_subscribe(const adamo_session_t *sess, const char *key);
adamo_sample_t *adamo_sub_recv(const adamo_subscriber_t *sub, uint64_t timeout_ms);adamo_sample_t *adamo_sub_try_recv(const adamo_subscriber_t *sub);
void adamo_sub_free(adamo_subscriber_t *sub);timeout_ms = 0 waits forever. adamo_sub_recv returns NULL on timeout (no error set) or on real failure (adamo_last_error() populated). adamo_sub_try_recv returns NULL with no error when the queue is empty.
Wildcards in key:
| Pattern | Matches |
|---|---|
my-arm/sensors/** | Everything under sensors/. |
my-arm/sensors/* | One level under sensors/. |
*/sensors/imu | IMU from any robot. |
Subscribe — callback
Section titled “Subscribe — callback”typedef void (*adamo_sample_cb_t)(const adamo_sample_t *sample, void *user);
adamo_cb_sub_t *adamo_subscribe_cb( const adamo_session_t *sess, const char *key, adamo_sample_cb_t cb, void *user);
void adamo_cb_sub_free(adamo_cb_sub_t *sub);The callback runs on a background receive thread. The sample pointer is borrowed — do not call adamo_sample_free from inside the callback. user must outlive the subscriber.
One-shot queries
Section titled “One-shot queries”adamo_sample_t **adamo_get( const adamo_session_t *sess, const char *key, uint64_t timeout_ms, // 0 → SDK default (60s) size_t *count_out);
void adamo_get_replies_free(adamo_sample_t **replies, size_t count);Collects every reply within timeout_ms. On success returns an array of *count_out sample pointers. Returns NULL on failure (set *count_out to 0).
adamo_get_replies_free frees each sample in the array and the array storage. NULL-safe.
Liveliness
Section titled “Liveliness”adamo_liveliness_token_t *adamo_liveliness_declare( const adamo_session_t *sess, const char *token_key);
void adamo_liveliness_token_free(adamo_liveliness_token_t *tok);Declare yourself alive at {token_key}/alive. The token is active until freed.
char **adamo_liveliness_get( const adamo_session_t *sess, const char *pattern, // e.g. "**/alive" size_t *count_out);
void adamo_liveliness_tokens_free(char **tokens, size_t count);One-shot query for currently-live tokens. Returns an array of *count_out NUL-terminated strings (prefix-stripped). Free with adamo_liveliness_tokens_free, which frees each string and the array storage.
typedef void (*adamo_liveliness_cb_t)(const char *key, int32_t alive, void *user);
adamo_liveliness_sub_t *adamo_liveliness_subscribe( const adamo_session_t *sess, const char *pattern, int32_t history, // 1 = deliver current set up front, 0 = changes only adamo_liveliness_cb_t cb, void *user);
void adamo_liveliness_sub_free(adamo_liveliness_sub_t *sub);Watch for liveliness changes. The callback fires with (key, 1) when a token appears and (key, 0) when it disappears. key is valid only for the duration of the call. Runs on a background receive thread; user must outlive the subscriber.
Fabric time
Section titled “Fabric time”uint64_t adamo_fabric_now_us(void);int32_t adamo_fabric_synced(void); // 1 once the first sync completesMicroseconds since the Unix epoch on the adamo fabric clock — the shared time axis every node on the network sees. Use these instead of gettimeofday() whenever a timestamp will be subtracted from a stamp produced on a different node.
Until the first sync, adamo_fabric_now_us falls back to the local wall clock.
These are free functions — they don’t take a session, so any code on the host can stamp timestamps consistently.
Video — ADAMO_BUILD_VIDEO=ON
Section titled “Video — ADAMO_BUILD_VIDEO=ON”When the library is built with the video feature, the following are available:
adamo_robot_t *adamo_robot_new_default( const char *api_key, const char *name // may be NULL — random name assigned);
adamo_robot_t *adamo_robot_new( const char *api_key, const char *name, // may be NULL — random name assigned adamo_protocol_t protocol);
void adamo_robot_free(adamo_robot_t *robot); // NULL-safeV4L2 capture
Section titled “V4L2 capture”int32_t adamo_robot_attach_video_v4l2( adamo_robot_t *robot, const char *name, // track name const char *device, // "/dev/video0" uint32_t width, uint32_t height, uint32_t fps, uint32_t bitrate_kbps, bool stereo);The SDK owns capture and encoding; frames never cross back into C. Returns 0 on success, -1 on failure.
Caller-fed shared-memory track
Section titled “Caller-fed shared-memory track”adamo_video_track_t *adamo_robot_video( adamo_robot_t *robot, const char *name, uint32_t width, uint32_t height, const char *pixel_format, // "BGRA", "RGB", "I420", "NV12", ... uint32_t fps, uint32_t bitrate_kbps, bool stereo);
int32_t adamo_video_track_send( adamo_video_track_t *track, const uint8_t *payload, uintptr_t payload_len // must equal width × height × bytes-per-pixel);
void adamo_video_track_free(adamo_video_track_t *track); // NULL-safePush raw frames yourself. Useful for perception pipelines and custom drivers.
int32_t adamo_robot_run(adamo_robot_t *robot); // blocks driving the pipelineBlocks indefinitely. Call from a dedicated thread. Consumes the robot — multiple run calls on the same handle are an error.
Encoder detection
Section titled “Encoder detection”const char *adamo_detect_encoder(void);Returns the best available H.264 encoder name for the host (NVENC, VA-API, VideoToolbox, x264) or "none". Pointer valid for the lifetime of the process.
Memory ownership summary
Section titled “Memory ownership summary”| Handle / pointer | Owner | Free function |
|---|---|---|
adamo_session_t * | C caller | adamo_session_free |
adamo_publisher_t * | C caller | adamo_publisher_free (undeclares first) |
adamo_subscriber_t * | C caller | adamo_sub_free |
adamo_cb_sub_t * | C caller | adamo_cb_sub_free |
adamo_sample_t * (returned) | C caller | adamo_sample_free |
adamo_sample_t * (in callback) | borrowed | do not free |
adamo_sample_t ** array (from adamo_get) | C caller | adamo_get_replies_free |
adamo_liveliness_token_t * | C caller | adamo_liveliness_token_free (undeclares first) |
char ** array (from adamo_liveliness_get) | C caller | adamo_liveliness_tokens_free |
adamo_liveliness_sub_t * | C caller | adamo_liveliness_sub_free |
adamo_robot_t * | C caller (until adamo_robot_run) | adamo_robot_free |
adamo_video_track_t * | C caller | adamo_video_track_free |
const char * from adamo_session_org / adamo_last_error / adamo_detect_encoder | borrowed | do not free |
C++17 wrapper — adamo.hpp
Section titled “C++17 wrapper — adamo.hpp”A header-only wrapper providing move-only RAII handles and exception-based error propagation. Mirrors the C API one-to-one.
#include "adamo/adamo.hpp"#include <iostream>
int main() { auto sess = adamo::Session::open("ak_your_key");
sess.put("my-robot/sensors/temperature", "22.5");
auto sub = sess.subscribe("my-robot/sensors/**"); while (auto sample = sub.recv(/* timeout_ms */ 5000)) { std::cout << sample->key() << ": " << sample->payload_len() << " bytes\n"; }}namespace adamo { enum class Protocol : int { Quic, Udp, Tcp }; inline constexpr Protocol default_protocol = Protocol::Quic;
class Error : public std::runtime_error;
struct SampleView { // borrowed view (callbacks) std::string_view key; const uint8_t *payload; std::size_t payload_len; bool is_delete;
static SampleView from_c(const adamo_sample_t *s); };
class Sample; // owned, move-only class Publisher; // move-only RAII class Subscriber; // move-only RAII class CallbackSubscriber; // move-only RAII, owns the trampoline class LivelinessToken; // move-only RAII class LivelinessSubscriber; // move-only RAII, owns the trampoline class Session; // move-only RAII
#ifdef ADAMO_HAS_VIDEO class VideoTrack; // move-only RAII class Robot; // move-only RAII inline std::string_view detect_encoder();#endif}Session
Section titled “Session”class Session {public: static Session open(const std::string &api_key); static Session open(const std::string &api_key, Protocol p);
std::string_view org() const;
void put(const std::string &key, const uint8_t *data, std::size_t len, std::uint8_t priority = 4, bool express = false); void put(const std::string &key, std::string_view s, ...);
Publisher publisher(const std::string &key, std::uint8_t priority = 4, bool express = false, bool reliable = true);
Subscriber subscribe(const std::string &key);
template <class F> CallbackSubscriber subscribe_cb(const std::string &key, F &&cb); // F is callable as void(SampleView)
std::vector<Sample> get(const std::string &key, std::uint64_t timeout_ms = 0);
LivelinessToken alive(const std::string &token_key);
std::vector<std::string> live_tokens(const std::string &pattern = "**/alive");
template <class F> LivelinessSubscriber on_liveliness(const std::string &pattern, F &&cb, bool history = true); // F is callable as void(std::string_view, bool)};All methods throw adamo::Error on failure.
Sample
Section titled “Sample”class Sample {public: std::string_view key() const; const uint8_t *payload() const noexcept; std::size_t payload_len() const noexcept; bool is_delete() const noexcept; std::vector<uint8_t> payload_vec() const;
explicit operator bool() const noexcept;};Publisher
Section titled “Publisher”class Publisher {public: void put(const uint8_t *data, std::size_t len); void put(std::string_view s); void put(const std::vector<uint8_t> &v);};Subscriber
Section titled “Subscriber”class Subscriber {public: std::optional<Sample> recv(std::uint64_t timeout_ms = 0); std::optional<Sample> try_recv();};Empty optional means timeout (recv) or empty queue (try_recv). Real errors throw.
LivelinessToken / LivelinessSubscriber
Section titled “LivelinessToken / LivelinessSubscriber”Both are RAII move-only handles. LivelinessToken keeps the token alive until destruction; LivelinessSubscriber keeps the callback registered until destruction.
Video — Robot and VideoTrack
Section titled “Video — Robot and VideoTrack”class Robot {public: static Robot create(const std::string &api_key, std::optional<std::string> name); static Robot create(const std::string &api_key, std::optional<std::string> name, Protocol p);
VideoTrack video(const std::string &name, std::uint32_t width, std::uint32_t height, const std::string &pixel_format, std::uint32_t fps, std::uint32_t bitrate_kbps, bool stereo = false);
void attach_v4l2(const std::string &name, const std::string &device, std::uint32_t width, std::uint32_t height, std::uint32_t fps, std::uint32_t bitrate_kbps, bool stereo = false);
int run() &&; // consumes the Robot; returns adamo_robot_run's int};
class VideoTrack {public: void send(const uint8_t *data, std::size_t len);};Define ADAMO_HAS_VIDEO in your build to enable these — the CMake build does this automatically when ADAMO_BUILD_VIDEO=ON.