A Rust async client for X-Plane's built-in UDP protocol.
Communicate with a running X-Plane instance to read and set datarefs, stream aircraft position, execute commands, manage aircraft and objects, etc. All over UDP with no plugins required.
NOTE: work in progress, alpha version, public API subject to change.
- Read datarefs at a configurable frequency
- Set datarefs
- Execute commands
- Stream aircraft position and weather radar
- Drive X-Plane visuals from an external flight model
- Send data output values
- Manage aircraft: load, place, load-and-place
- Auto-discover X-Plane on the local network via UDP beacon
- Fully async
[dependencies]
xpudpc = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }In Settings → Network, make sure Accept incoming connections is enabled (required on X-Plane 12).
use std::time::Duration;
use xpudpc::{Response, XPlaneClient};
#[tokio::main]
async fn main() -> xpudpc::Result<()> {
let client = XPlaneClient::connect("192.168.1.10:49000").await?;
// Subscribe to indicated airspeed — index 0 is our local identifier
client.subscribe_dataref(10, 0, "sim/cockpit2/gauges/indicators/airspeed_kts_pilot").await?;
loop {
if let Ok(Response::DatarefValues(refs)) = client.recv_timeout(Duration::from_secs(1)).await {
for r in refs {
println!("airspeed: {:.1} kts", r.value);
}
}
}
}X-Plane broadcasts a UDP beacon on the local network. XPlaneClient::auto
finds a running instance and connects in one call:
use std::time::Duration;
use xpudpc::XPlaneClient;
#[tokio::main]
async fn main() -> xpudpc::Result<()> {
let client = XPlaneClient::auto(Some(Duration::from_secs(30))).await?;
// ...
Ok(())
}If you need the beacon details (version, computer name, etc.) before
connecting, use Beacon::find directly:
use std::time::Duration;
use xpudpc::{Beacon, XPlaneClient};
#[tokio::main]
async fn main() -> xpudpc::Result<()> {
let beacon = Beacon::find(Some(Duration::from_secs(30))).await?;
println!("Found X-Plane v{} at {}:{}", beacon.version_number, beacon.ip, beacon.port);
let client = XPlaneClient::connect((beacon.ip, beacon.port)).await?;
// ...
Ok(())
}All incoming data arrives through recv() (or recv_timeout()) as a
Response enum. Set up one or more streams before entering the receive loop:
// Position at 30 Hz
client.request_position(30).await?;
// Two datarefs, each with a local index for identification
client.subscribe_dataref(10, 0, "sim/cockpit2/gauges/indicators/airspeed_kts_pilot").await?;
client.subscribe_dataref(5, 1, "sim/cockpit2/gauges/indicators/altitude_ft_pilot").await?;
loop {
match client.recv_timeout(Duration::from_secs(3)).await? {
Response::Position(pos) => {
println!("lat={:.5} lon={:.5} alt={:.0}m hdg={:.1}°",
pos.latitude, pos.longitude, pos.elevation, pos.heading);
}
Response::DatarefValues(refs) => {
for r in refs {
match r.index {
0 => println!("airspeed : {:.1} kts", r.value),
1 => println!("altitude : {:.0} ft", r.value),
_ => {}
}
}
}
_ => {}
}
}Unsubscribe or stop streams when no longer needed:
client.unsubscribe_dataref(0).await?;
client.stop_position().await?;All X-Plane datarefs accept float values; pass 1.0 or 0.0 for boolean
switches:
client.set_dataref("sim/cockpit/switches/anti_ice_surf_heat_left", 1.0).await?;client.send_command("sim/flight_controls/flaps_up").await?;
client.send_command("sim/flight_controls/landing_gear_toggle").await?;Command strings can be found in X-Plane under Settings → Joystick → Buttons: Advanced.
use xpudpc::{PlacementConfig, StartType};
client.place_aircraft(&PlacementConfig {
start_type: StartType::SpecifyLatLonEle,
latitude: 37.6189,
longitude: -122.3750,
elevation: 4.0,
heading: 280.0,
..Default::default()
}).await?;use xpudpc::Pose;
let pose = Pose { lat, lon, ele, heading, pitch, roll };
// Override X-Plane's flight model entirely (VEHX)
client.drive_visuals(0, pose).await?;
// Or move the aircraft once without overriding the flight model (VEHS)
client.move_aircraft(0, pose).await?;client.fail_system("0").await?; // fail the first system in the failure list
client.recover_system("0").await?;
client.recover_all().await?; // reset everything at once| Variant | Triggered by |
|---|---|
Response::Position(AircraftPosition) |
request_position |
Response::Radar(Vec<RadarPoint>) |
request_radar |
Response::DatarefValues(Vec<DatarefValue>) |
subscribe_dataref |
Response::Data(DataOutput) |
select_data or X-Plane data output screen |
# Stream position at 1 Hz
cargo run --example position
# Stream COM1 radio state
cargo run --example com1- X-Plane listens on port 49000 by default.
- Replies are sent back to whichever IP/port the request came from.
- All values are little-endian.
- Frequencies for RPOS and RADR are transmitted as null-terminated ASCII strings.
- The UDP beacon is multicast on group
239.255.1.1:49707.