OddSockets C++ SDK

Modern C++ SDK for embedded systems and IoT devices - JavaScript Pattern Compliant

CMake Ready C++17 Embedded/IoT High Performance JS Pattern Compliant

Overview & Features

The OddSockets C++ SDK provides a powerful, resource-efficient interface for real-time messaging specifically designed for embedded systems and IoT devices while maintaining full compatibility with the JavaScript SDK pattern.

Embedded Optimized

Minimal memory footprint, configurable resource limits, and custom allocator support for embedded systems.

Modern C++17

Clean, type-safe API with RAII principles, smart pointers, and modern async programming.

JS Pattern Compliant

100% architectural compliance with JavaScript SDK - same API patterns and behavior.

High Performance

Optimized for low latency with efficient WebSocket connections and minimal overhead.

Cross-Platform

Works on Linux, Windows, macOS, and embedded platforms with consistent behavior.

Production Ready

Thread-safe, automatic failover, and comprehensive error handling for mission-critical applications.

Installation

cmake
# CMakeLists.txt
find_package(OddSockets REQUIRED)
target_link_libraries(your_target OddSockets::oddsockets)
bash
vcpkg install oddsockets-cpp-sdk
ini
# conanfile.txt
[requires]
oddsockets-cpp-sdk/1.0.0

[generators]
cmake
bash
git clone https://github.com/oddsockets/cpp-sdk.git
cd cpp-sdk
mkdir build && cd build
cmake ..
make install

Quick Start

Basic Usage

cpp
#include 
#include 

int main() {
    // Create configuration
    oddsockets::Config config;
    config.apiKey = "ak_live_1234567890abcdef";
    config.userId = "user123";
    
    // Create client
    auto client = std::make_unique(config);
    
    // Connect to platform
    auto connectFuture = client->connect();
    bool connected = connectFuture.get();
    
    if (connected) {
        // Get a channel
        auto channel = client->channel("my-channel");
        
        // Subscribe to messages
        channel->subscribe([](const std::string& message) {
            std::cout << "Received: " << message << std::endl;
        });
        
        // Publish a message
        auto publishFuture = channel->publish("Hello from C++!");
        auto result = publishFuture.get();
        
        if (result.success) {
            std::cout << "Message published successfully!" << std::endl;
        }
    }
    
    return 0;
}

Embedded System Usage

cpp
#include 

// Custom allocator for embedded systems
class EmbeddedAllocator {
public:
    static void* allocate(size_t size) {
        return malloc(size);
    }
    
    static void deallocate(void* ptr) {
        free(ptr);
    }
};

int main() {
    // Set custom allocator
    oddsockets::setCustomAllocator();
    
    // Minimal configuration for embedded systems
    oddsockets::Config config;
    config.apiKey = "ak_live_1234567890abcdef";
    config.maxChannels = 4;           // Limit channels
    config.maxMessageSize = 1024;     // Smaller messages
    config.enableLogging = false;     // Disable logging
    config.enableSSL = false;         // Disable SSL if not needed
    config.enableThreading = false;   // Single-threaded mode
    
    auto client = std::make_unique(config);
    
    // Connect and use...
    auto connectFuture = client->connect();
    bool connected = connectFuture.get();
    
    if (connected) {
        auto channel = client->channel("iot-data");
        
        // Process events manually in single-threaded mode
        while (client->isConnected()) {
            client->processEvents();
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    
    return 0;
}

Async/Await Style (C++20)

cpp
#include 
#include 

// C++20 coroutine support (optional)
oddsockets::task chatExample() {
    oddsockets::Config config;
    config.apiKey = "ak_live_1234567890abcdef";
    
    auto client = std::make_unique(config);
    
    // Await connection
    bool connected = co_await client->connect();
    
    if (connected) {
        auto channel = client->channel("chat");
        
        // Await subscription
        bool subscribed = co_await channel->subscribe([](const std::string& msg) {
            std::cout << "Message: " << msg << std::endl;
        });
        
        if (subscribed) {
            // Await publish
            auto result = co_await channel->publish("Hello from C++20!");
            std::cout << "Published: " << result.success << std::endl;
        }
    }
}

Configuration

Client Configuration

cpp
oddsockets::Config config;

// Required
config.apiKey = "ak_live_1234567890abcdef";

// Optional
config.userId = "user123";
config.managerUrl = "https://manager.oddsockets.com";

// Connection Options
config.autoConnect = true;
config.reconnectAttempts = 5;
config.reconnectDelayMs = 1000;
config.connectionTimeoutMs = 10000;
config.messageTimeoutMs = 5000;

// Resource Limits (for embedded systems)
config.maxChannels = 32;
config.maxMessageSize = 32768;  // 32KB

// SSL/TLS Options
config.enableSsl = true;
config.sslVerifyPeer = true;
config.caCertPath = "/path/to/ca-cert.pem";

// Logging
config.logLevel = oddsockets::LogLevel::Info;
config.logCallback = [](oddsockets::LogLevel level, const std::string& message) {
    std::cout << "[" << oddsockets::logLevelToString(level) << "] " << message << std::endl;
};

// Callbacks
config.connectionCallback = [](oddsockets::ConnectionState state) {
    std::cout << "Connection: " << oddsockets::connectionStateToString(state) << std::endl;
};

config.errorCallback = [](oddsockets::ErrorCode error, const std::string& message) {
    std::cout << "Error: " << message << std::endl;
};

Channel Options

cpp
// Subscribe options
oddsockets::SubscribeOptions subscribeOpts;
subscribeOpts.maxHistory = 100;
subscribeOpts.retainHistory = true;
subscribeOpts.enablePresence = true;

channel->subscribe(callback, subscribeOpts);

// Publish options
oddsockets::PublishOptions publishOpts;
publishOpts.ttlSeconds = 3600;
publishOpts.metadata = R"({"priority": "high"})";
publishOpts.storeInHistory = true;

channel->publish("Hello, World!", publishOpts);

// History options
oddsockets::HistoryOptions historyOpts;
historyOpts.count = 50;
historyOpts.startTime = "2024-01-01T00:00:00Z";
historyOpts.endTime = "2024-01-02T00:00:00Z";

auto historyFuture = channel->getHistory(historyOpts);

Embedded Systems & IoT

The C++ SDK is specifically optimized for embedded systems and IoT devices with minimal resource requirements:

Memory Management

Custom allocators, memory tracking, and configurable limits for resource-constrained environments.

Single-Threaded Mode

Optional single-threaded operation for systems without threading support.

No-Exception Mode

Error code-based error handling for systems that don't support exceptions.

Configurable Features

Disable SSL, logging, or other features to reduce binary size and memory usage.

Memory Optimization

cpp
// Custom memory allocator
class PoolAllocator {
    static constexpr size_t POOL_SIZE = 64 * 1024; // 64KB pool
    static uint8_t pool[POOL_SIZE];
    static size_t offset;
    
public:
    static void* allocate(size_t size) {
        if (offset + size > POOL_SIZE) return nullptr;
        void* ptr = pool + offset;
        offset += size;
        return ptr;
    }
    
    static void deallocate(void* ptr) {
        // Pool allocator - no individual deallocation
    }
    
    static void reset() {
        offset = 0;
    }
};

// Set custom allocator
oddsockets::setCustomAllocator();

// Monitor memory usage
size_t allocated, peak;
oddsockets::getMemoryStats(&allocated, &peak);
std::cout << "Memory: " << allocated << " bytes (peak: " << peak << ")" << std::endl;

Minimal Configuration

cpp
// Minimal embedded configuration
oddsockets::Config config;
config.apiKey = "ak_live_1234567890abcdef";

// Resource limits
config.maxChannels = 2;           // Only 2 channels
config.maxMessageSize = 512;      // 512 byte messages
config.connectionTimeoutMs = 5000; // 5 second timeout

// Disable features
config.enableLogging = false;     // No logging
config.enableSSL = false;         // No SSL (if not needed)
config.enableThreading = false;   // Single-threaded
config.enableExceptions = false;  // No exceptions

// Minimal callbacks
config.errorCallback = [](oddsockets::ErrorCode error, const std::string&) {
    // Handle error codes instead of exceptions
    if (error != oddsockets::ErrorCode::Success) {
        // Handle error...
    }
};

Platform Support

Supported Platforms

  • Linux (ARM, x86, x64)
  • FreeRTOS
  • Zephyr RTOS
  • Arduino (ESP32, ESP8266)
  • Raspberry Pi
  • STM32
  • Nordic nRF
  • Windows IoT

Requirements

  • C++17 compiler
  • 32KB+ RAM (configurable)
  • TCP/IP stack
  • Optional: SSL/TLS support
  • Optional: Threading support

Examples

Explore comprehensive examples demonstrating the OddSockets C++ SDK in action:

Performance & Compatibility

OddSockets C++ SDK delivers superior performance optimized for embedded systems:

<10ms
Latency
99.9%
Uptime
32KB
Min RAM
100K+
Messages/sec

Compiler Support

  • GCC 7+ (C++17)
  • Clang 6+ (C++17)
  • MSVC 2019+ (C++17)
  • ARM GCC (embedded)

Build Systems

  • CMake 3.12+
  • vcpkg
  • Conan
  • Arduino IDE

Platform Integrations

The OddSockets C++ SDK works seamlessly across multiple platforms and environments:

Arduino (ESP32)

cpp
#include 
#include 

const char* ssid = "your-wifi-ssid";
const char* password = "your-wifi-password";

std::unique_ptr client;

void setup() {
    Serial.begin(115200);
    
    // Connect to WiFi
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println("Connecting to WiFi...");
    }
    
    // Configure OddSockets for ESP32
    oddsockets::Config config;
    config.apiKey = "ak_live_1234567890abcdef";
    config.maxChannels = 2;
    config.maxMessageSize = 1024;
    config.enableLogging = false;
    
    client = std::make_unique(config);
    
    // Connect and subscribe
    auto connectFuture = client->connect();
    if (connectFuture.get()) {
        auto channel = client->channel("sensor-data");
        
        channel->subscribe([](const std::string& message) {
            Serial.println(("Received: " + message).c_str());
        });
    }
}

void loop() {
    // Process events
    client->processEvents();
    
    // Send sensor data every 10 seconds
    static unsigned long lastSend = 0;
    if (millis() - lastSend > 10000) {
        auto channel = client->channel("sensor-data");
        
        String sensorData = "{\"temperature\": " + String(random(20, 30)) + 
                           ", \"humidity\": " + String(random(40, 60)) + "}";
        
        channel->publish(sensorData.c_str());
        lastSend = millis();
    }
    
    delay(10);
}

Raspberry Pi

cpp
#include 
#include 
#include 
#include 

const int LED_PIN = 18;
const int BUTTON_PIN = 24;

int main() {
    // Initialize WiringPi
    wiringPiSetupGpio();
    pinMode(LED_PIN, OUTPUT);
    pinMode(BUTTON_PIN, INPUT);
    pullUpDnControl(BUTTON_PIN, PUD_UP);
    
    // Configure OddSockets
    oddsockets::Config config;
    config.apiKey = "ak_live_1234567890abcdef";
    config.userId = "raspberry-pi-001";
    
    auto client = std::make_unique(config);
    
    // Connect
    auto connectFuture = client->connect();
    if (!connectFuture.get()) {
        std::cerr << "Failed to connect!" << std::endl;
        return 1;
    }
    
    auto controlChannel = client->channel("device-control");
    auto statusChannel = client->channel("device-status");
    
    // Subscribe to control commands
    controlChannel->subscribe([](const std::string& message) {
        std::cout << "Control command: " << message << std::endl;
        
        if (message == "LED_ON") {
            digitalWrite(LED_PIN, HIGH);
        } else if (message == "LED_OFF") {
            digitalWrite(LED_PIN, LOW);
        }
    });
    
    // Monitor button and send status
    bool lastButtonState = HIGH;
    
    while (client->isConnected()) {
        client->processEvents();
        
        // Check button state
        bool buttonState = digitalRead(BUTTON_PIN);
        if (buttonState != lastButtonState && buttonState == LOW) {
            statusChannel->publish("BUTTON_PRESSED");
            std::cout << "Button pressed!" << std::endl;
        }
        lastButtonState = buttonState;
        
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    
    return 0;
}

FreeRTOS

cpp
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include 

// FreeRTOS allocator
class FreeRTOSAllocator {
public:
    static void* allocate(size_t size) {
        return pvPortMalloc(size);
    }
    
    static void deallocate(void* ptr) {
        vPortFree(ptr);
    }
};

std::unique_ptr client;
QueueHandle_t messageQueue;

void oddSocketsTask(void* parameters) {
    // Set FreeRTOS allocator
    oddsockets::setCustomAllocator();
    
    // Configure for FreeRTOS
    oddsockets::Config config;
    config.apiKey = "ak_live_1234567890abcdef";
    config.maxChannels = 1;
    config.maxMessageSize = 256;
    config.enableLogging = false;
    config.enableThreading = false;  // Use FreeRTOS tasks instead
    
    client = std::make_unique(config);
    
    // Connect
    auto connectFuture = client->connect();
    if (connectFuture.get()) {
        auto channel = client->channel("freertos-device");
        
        channel->subscribe([](const std::string& message) {
            // Send message to queue for processing by other tasks
            const char* msg = message.c_str();
            xQueueSend(messageQueue, &msg, portMAX_DELAY);
        });
        
        // Main event loop
        while (1) {
            client->processEvents();
            vTaskDelay(pdMS_TO_TICKS(10));
        }
    }
    
    vTaskDelete(NULL);
}

void sensorTask(void* parameters) {
    while (1) {
        // Read sensor data
        float temperature = readTemperatureSensor();
        float humidity = readHumiditySensor();
        
        // Create JSON message
        char buffer[128];
        snprintf(buffer, sizeof(buffer), 
                "{\"temp\": %.2f, \"humidity\": %.2f}", 
                temperature, humidity);
        
        // Publish sensor data
        if (client && client->isConnected()) {
            auto channel = client->channel("freertos-device");
            channel->publish(std::string(buffer));
        }
        
        vTaskDelay(pdMS_TO_TICKS(30000)); // 30 seconds
    }
}

int main() {
    // Create message queue
    messageQueue = xQueueCreate(10, sizeof(char*));
    
    // Create tasks
    xTaskCreate(oddSocketsTask, "OddSockets", 4096, NULL, 2, NULL);
    xTaskCreate(sensorTask, "Sensor", 2048, NULL, 1, NULL);
    
    // Start scheduler
    vTaskStartScheduler();
    
    return 0;
}

Cross-Platform Desktop

cpp
#include 
#include 
#include 
#include 

#ifdef _WIN32
    #include 
#else
    #include 
#endif

class CrossPlatformApp {
private:
    std::unique_ptr client;
    std::atomic running{true};
    
public:
    void run() {
        // Configure client
        oddsockets::Config config;
        config.apiKey = "ak_live_1234567890abcdef";
        config.userId = getSystemId();
        
        // Platform-specific optimizations
        #ifdef __EMBEDDED__
            config.maxChannels = 2;
            config.enableLogging = false;
        #else
            config.maxChannels = 10;
            config.enableLogging = true;
        #endif
        
        client = std::make_unique(config);
        
        // Connect
        auto connectFuture = client->connect();
        if (!connectFuture.get()) {
            std::cerr << "Failed to connect!" << std::endl;
            return;
        }
        
        std::cout << "Connected to OddSockets!" << std::endl;
        
        // Setup channels
        auto chatChannel = client->channel("cross-platform-chat");
        auto statusChannel = client->channel("system-status");
        
        // Subscribe to chat
        chatChannel->subscribe([this](const std::string& message) {
            std::cout << "Chat: " << message << std::endl;
        });
        
        // Send periodic status updates
        std::thread statusThread([this, statusChannel]() {
            while (running) {
                std::string status = getSystemStatus();
                statusChannel->publish(status);
                
                std::this_thread::sleep_for(std::chrono::seconds(60));
            }
        });
        
        // Main loop
        std::string input;
        while (running && std::getline(std::cin, input)) {
            if (input == "/quit") {
                running = false;
                break;
            }
            
            chatChannel->publish(input);
        }
        
        statusThread.join();
        client->disconnect();
    }
    
private:
    std::string getSystemId() {
        #ifdef _WIN32
            char computerName[MAX_COMPUTERNAME_LENGTH + 1];
            DWORD size = sizeof(computerName);
            GetComputerNameA(computerName, &size);
            return std::string(computerName);
        #else
            char hostname[256];
            gethostname(hostname, sizeof(hostname));
            return std::string(hostname);
        #endif
    }
    
    std::string getSystemStatus() {
        // Platform-specific system info
        return R"({"platform": ")" + getPlatformName() + R"(", "uptime": )" + 
               std::to_string(getUptime()) + "}";
    }
    
    std::string getPlatformName() {
        #ifdef _WIN32
            return "Windows";
        #elif __linux__
            return "Linux";
        #elif __APPLE__
            return "macOS";
        #else
            return "Unknown";
        #endif
    }
    
    long getUptime() {
        #ifdef _WIN32
            return GetTickCount64() / 1000;
        #else
            return time(nullptr);
        #endif
    }
};

int main() {
    CrossPlatformApp app;
    app.run();
    return 0;
}