To use R2R, you must first source your ROS2 environment. R2R uses C introspection to build bindings to the RCL and existing message types without requiring the ROS2 build infrastructure.
This example demonstrates how to initialize a Context, create a Node, and set up an asynchronous publisher and subscriber using a LocalPool executor.
use futures::{executor::LocalPool, future, stream::StreamExt, task::LocalSpawnExt};
use r2r::QosProfile;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "node", "namespace")?;
let subscriber =
node.subscribe::<r2r::std_msgs::msg::String>("/topic", QosProfile::default())?;
let publisher =
node.create_publisher::<r2r::std_msgs::msg::String>("/topic", QosProfile::default())?;
let mut timer = node.create_wall_timer(std::time::Duration::from_millis(1000))?;
// Set up a simple task executor.
let mut pool = LocalPool::new();
let spawner = pool.spawner();
// Run the subscriber in one task, printing the messages
spawner.spawn_local(async move {
subscriber
.for_each(|msg| {
println!("got new msg: {}", msg.data);
future::ready(())
})
.await
})?;
// Run the publisher in another task
spawner.spawn_local(async move {
let mut counter = 0;
loop {
let _elapsed = timer.tick().await.unwrap();
let msg = r2r::std_msgs::msg::String {
data: format!("Hello, world! ({})", counter),
};
publisher.publish(&msg).unwrap();
counter += 1;
}
})?;
// Main loop spins ros.
loop {
node.spin_once(std::time::Duration::from_millis(100));
pool.run_until_stalled();
}
}