#include #include #include #include #include #include #include #include #include #include #include "components/ffmsep/cpstream_core.hh" #include "components/ffmsep/tactile/tacdec.hh" using namespace std::chrono_literals; static volatile std::sig_atomic_t g_running = 1; void handle_sigint(int) { g_running = 0; } int main(int argc, char** argv) { std::signal(SIGINT, handle_sigint); // Register tactile codec so the stream can resolve it by ID or name. ffmsep::tactile::register_tactile_codec(); const auto ports = ffmsep::CPStreamCore::list_available_ports(); const auto describe_ports = [&ports]() { if (ports.empty()) { std::cout << "No serial ports detected.\n"; return; } std::cout << "Available serial ports:\n"; for (const auto& p : ports) { std::cout << " - " << p.port.c_str() << " (" << p.description.c_str() << ")\n"; } }; std::string port; if (argc >= 2) { const std::string requested = argv[1]; if (ports.empty()) { std::cout << "No serial ports detected, attempting requested '" << requested << "'.\n"; port = requested; } else { const auto it = std::find_if(ports.begin(), ports.end(), [&](const auto& info) { return info.port == requested; }); if (it == ports.end()) { std::cerr << "Requested port '" << requested << "' not found among detected ports.\n"; describe_ports(); return 1; } port = it->port; } } else { describe_ports(); if (!ports.empty()) { port = ports.front().port; std::cout << "Auto-selecting: " << port << "\n"; } else { std::cerr << "No serial ports found. Exiting.\n"; return 1; } } ffmsep::CPStreamConfig cfg; cfg.port = port; cfg.baudrate = 115200; cfg.codec_id = ffmsep::CPCodecID::Tactile; // resolve tactile decoder cfg.read_chunk_size = 256; cfg.packet_queue_capacity = 128; cfg.frame_queue_capacity = 32; cfg.slave_request_command = { 0x55, 0xAA, 0x09, 0x00, 0x34, 0x00, 0xFB, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00, 0x7A }; cfg.slave_request_interval = 200ms; ffmsep::CPStreamCore core(cfg); if (!core.open()) { std::cerr << "Open failed: " << core.last_error() << "\n"; return 2; } if (!core.start()) { std::cerr << "Start failed: " << core.last_error() << "\n"; return 3; } auto print_hex = [](const std::vector& data) { if (data.empty()) { std::cout << "(empty)"; return; } const auto old_flags = std::cout.flags(); const auto old_fill = std::cout.fill(); std::cout << std::hex << std::uppercase; for (std::size_t idx = 0; idx < data.size(); ++idx) { if (idx != 0U) { std::cout << ' '; } std::cout << std::setw(2) << std::setfill('0') << static_cast(data[idx]); } std::cout.flags(old_flags); std::cout.fill(old_fill); }; std::cout << "Streaming from " << port << ". Press Ctrl+C to stop...\n"; if (!cfg.slave_request_command.empty()) { std::cout << "Slave mode active; request interval " << cfg.slave_request_interval.count() << " ms.\n"; } // Also demonstrate polling API (in case users don't want callbacks) while (g_running) { std::shared_ptr df; if (core.wait_for_frame(df, 200ms) && df) { std::cout << "Frame pts=" << df->pts << " bytes=" << df->frame.data.size(); if (df->tactile) { const auto& tf = *df->tactile; std::cout << " addr=" << int(tf.device_address) << " func=0x" << std::hex << std::uppercase << int(tf.response_function) << std::dec; if (df->tactile_matrix_size) { const auto& ms = *df->tactile_matrix_size; std::cout << " matrix=" << int(ms.long_edge) << "x" << int(ms.short_edge); } if (!df->tactile_pressures.empty()) { std::cout << " pressures=" << df->tactile_pressures.size() << " values=["; const std::size_t preview = std::min(df->tactile_pressures.size(), 8); for (std::size_t idx = 0; idx < preview; ++idx) { if (idx != 0U) { std::cout << ", "; } std::cout << df->tactile_pressures[idx]; } if (preview < df->tactile_pressures.size()) { std::cout << ", ..."; } std::cout << "]"; } std::cout << "\n raw="; print_hex(df->frame.data); } else { std::cout << " raw="; print_hex(df->frame.data); } std::cout << "\n"; } } core.stop(); core.close(); std::cout << "Stopped.\n"; return 0; }