#middleware #debugging #embedded-linux #networking #autonomy

bissel

Robotics-focused publish-subscribe/request-reply middleware for embedded Linux

2 unstable releases

0.2.0 Feb 7, 2022
0.1.0 Jan 27, 2022

#162 in Robotics

MPL-2.0 license

40KB
771 lines

crates.io Documentation CI

bissel

bissel is an experimental robotics-focused middleware for embedded Linux. It uses a star-shaped network topology, with a focus on ease-of-use and transparent design and operation. It is more similar to ZeroMQ than to higher-level frameworks like ROS/2, but uses central coordination process similar to MOOS-IvP. Bissel currently supports both publish/subscribe and publish/request messaging patterns over TCP.

Under the hood, bissel relies on:

  • sled: High-performance embedded, thread-safe database
  • tokio: Asynchronous runtime, enabling a large number of simultaneous connections
  • postcard: Efficient #![no_std]-compatible, serde-based de/serializer designed for embedded or constrained environments
use bissel::*;
use serde::{Deserialize, Serialize};

// Any type implementing Debug and serde's De/Serialize traits are Bissel-compatible
// (the standard library Debug and Clone traits are also required)
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Coordinate {
    x: f32,
    y: f32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The Host is running on localhost, but any network interface such as WiFi
    // or Ethernet are available as well
    let mut host: Host = HostConfig::new("lo")
        .socket_num(25_000)
        .store_filename("store")
        .build()
        .unwrap();
    host.start().unwrap();
    // Other tasks can operate while the host is running in the background
    
    // Build a Node
    let addr = "127.0.0.1:25000".parse::<std::net::SocketAddr>().unwrap();
    let node: Node<Idle, Coordinate> = NodeConfig::new("GPS_NODE")
        .topic("position")
        .host_addr(addr)
        .build()?;
    // Bissel Nodes use strict typestates; without using the connect() method first,
    // the compiler won't let you use the publish() or request() methods on an Idle Node
    let mut node: Node<Active, Coordinate> = node.connect()?;

    // Nodes can also be subscribers, which will request topic updates from the Host
    // at a given rate
    let subscriber = NodeConfig::<Coordinate>::new("GPS_SUBSCRIBER")
        .topic("position")
        .host_addr(addr)
        .build()?
        .subscribe(std::time::Duration::from_millis(100))?;

    let c = Coordinate { x: 4.0, y: 4.0 };
    node.publish(c).unwrap();

    // Since Nodes are statically-typed, the following lines would fail at 
    // compile-time due to type errors!
    // node.publish(1usize).unwrap()
    // let result: bool = node.request().unwrap();

    for i in 0..5 {
        // Could get this by reading a GPS, for example
        let c = Coordinate { x: i as f32, y: i as f32 };
        node.publish(c)?;
        std::thread::sleep(std::time::Duration::from_millis(100));
        let result = node.request()?;
        // or could use the value held by the subscribed node
        let subscription = subscriber.get_subscribed_data().unwrap();
        println!("Got position: {:?}", result);
    }

    // host.stop()?;
    Ok(())
}

Benchmarks

Preliminary benchmark data is showing round-trip message times (publish-request-reply) on locahost using the --release compilation profile, on the README's Coordinate data (strongly-typed, 8 bytes) to be ~100 microseconds.

Additional benchmarking information can be found using cargo run --release --example benchmark.

License

This library is licensed under the Mozilla Public License, version 2.0 (MPL-2.0)

Dependencies

~9–17MB
~207K SLoC