Introduction
Build scalable communication systems with Eclipse Zenoh
Get started with Zenoh on Raspberry Pi and Arm Linux
Containerize and deploy Zenoh across multiple Raspberry Pi devices
Run a simple Zenoh pub/sub example
Run a Zenoh storage and query example
Run a Zenoh queryable node for on-demand edge computation
Run a Zenoh queryable with parameterized Rust computation
Next Steps
Finally, you’ll combine pub/sub, storage, and queryable components to simulate a distributed computation flow—demonstrating how Zenoh enables intelligent, coordinated edge systems.
You’ll learn how to use Zenoh’s Queryable API in Rust to build a parameterized query system for estimating battery health at the edge.
This extends a previous example by supporting runtime query parameters like battery level and temperature.
In robotic fleet management, a central controller may need to assess each robot’s battery health on demand.
Instead of streaming data continuously, robots expose a queryable endpoint that returns a real-time health score based on current battery level and temperature.
This saves bandwidth and enables lightweight edge-side decision-making.
On any Raspberry Pi:
cd ~/zenoh
cargo new zenoh_battery_estimator
Update following dependencies setting by editing the file $HOME/zenoh/zenoh_battery_estimator/Cargo.toml
[dependencies]
zenoh = { path = "../zenoh" }
tokio = { version = "1", features = ["full"] }
url = "2"
Next, log in to the other Raspberry Pi.
Replace the contents of $HOME/zenoh/zenoh_battery_estimator/src/main.rs
with the code below.
use zenoh::{open, Config};
use std::collections::HashMap;
use url::form_urlencoded;
#[tokio::main]
async fn main() -> zenoh::Result<()> {
let session = open(Config::default()).await?;
let _queryable = session
.declare_queryable("robot/battery/estimate")
.callback(|query| {
tokio::spawn(async move {
let selector = query.selector();
let key = selector.key_expr();
let params = selector.parameters().as_str();
let decoded: HashMap<_, _> =
form_urlencoded::parse(params.as_bytes()).into_owned().collect();
let battery = decoded
.get("level")
.unwrap_or(&"50".to_string())
.parse::<u32>()
.unwrap_or(50);
let temp = decoded
.get("temp")
.unwrap_or(&"25".to_string())
.parse::<u32>()
.unwrap_or(25);
let health_score = 100 - (100 - battery) - ((temp.saturating_sub(25)) / 2);
let response = format!("Estimated battery health: {}%", health_score);
let _ = query.reply(key, response).await;
});
})
.await?;
println!("Queryable running on 'robot/battery/estimate'");
tokio::signal::ctrl_c().await.unwrap();
Ok(())
}
This edge node responds to real-time queries using Zenoh’s Queryable API. It listens for requests on the robot/battery/estimate key and returns a calculated battery health score based on provided input parameters.
The program starts by establishing a Zenoh session using open(Config::default()). It then registers a queryable resource on the robot/battery/estimate key. Whenever this key is queried, a callback function is invoked asynchronously using tokio::spawn.
Inside the callback:
You can extend this queryable pattern to respond to other real-time diagnostics, such as CPU load, camera snapshots, or local ML inference results.
This design pattern enables efficient, on-demand data exchange with minimal bandwidth usage. This is ideal for edge computing scenarios where resources and connectivity are constrained.
The health score is calculated using the following logic:
let health_score = 100 - (100 - battery) - ((temp.saturating_sub(25)) / 2);
This formula estimates battery health as a percentage, considering both battery level and temperature:
This logic computes battery health as a percentage, adjusting for elevated temperatures. If temperature exceeds 25°C, the score is reduced by 1% for every 2°C increase.
To ensure the calculation remains safe even when the temperature is below 25°C, the code uses saturating_sub(25), which prevents the result from becoming negative and avoids potential underflow errors.
For example, if battery = 88
and temp = 32
, then:
cd $HOME/zenoh/zenoh_battery_estimator
cargo build --release
After the build process, you will see:
Compiling zenoh_battery_estimator v1.4.0 (/home/ubuntu/zenoh_v1.4/zenoh_battery_estimator)
Finished `release` profile [optimized] target(s) in 1m 22s
Run it on the Raspberry Pi you used for the build run:
cd $HOME/zenoh/target/release/
./zenoh_battery_estimator
You can reuse the built-in Zenoh z_get
CLI from the other Raspberry Pi.
cd ~/zenoh/target/release/examples
./z_get -s "robot/battery/estimate?level=88&temp=32"
The result is shown below:
Figure 4: Dynamic Queryable with Computation
The expected output is:
>> Received ('robot/battery/estimate': 'Estimated battery health: 85%')
You’ve just built a responsive, parameterized edge compute service using Zenoh’s Queryable API in Rust. It’s lightweight but a powerful pattern for real-time intelligence at the edge.
This approach not only minimizes network overhead but also enables each device to process and respond to context-aware queries on demand.
It’s a strong foundation for building scalable, event-driven IoT systems that can adapt dynamically to operational needs.