# SunSpec Rust Implementation
[](https://crates.io/crates/sunspec)
[](https://github.com/bikeshedder/sunspec/actions?query=workflow%3ARust)

[](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html)
This [Rust](https://www.rust-lang.org) crate contains code for accessing [SunSpec](https://en.wikipedia.org/wiki/SunSpec) compliant devices
in a safe and convenient way.
## Highlights
- [x] Pure Rust library
- [x] No unsafe code
- [x] Panic free
- [x] All communication is abstracted via traits making it runtime agnostic
- [x] Supports Modbus TCP and RTU (via [tokio-modbus](https://crates.io/crates/tokio-modbus)).
- [x] Implements "Device Information Model Discovery" as
defined in the SunSpec specification.
- [x] Fully typed models generated from the JSON files contained in the
[SunSpec models repository](https://github.com/sunspec/models/)
- [x] Fully typed enums
- [x] Fully typed bitfields
- [x] Fully documented. Even the generated models.
- [x] Reading of complete models in a single request.
## Features
| `tokio` | Enable `tokio_modbus` support | `tokio-modbus`, `tokio/time` | yes |
| `serde` | Enable `serde` support | `serde`, `bitflags/serde` | yes |
## Examples
The `examples` directory in the code repository contains the unabridged code.
### Example code for accessing data from a three phase inverter using the model 103
```rust,ignore
use std::{error::Error, net::SocketAddr, time::Duration};
use clap::Parser;
use itertools::Itertools;
use sunspec::{
client::{AsyncClient, Config},
models::{model1::Model1, model103::Model103},
};
use tokio::time::sleep;
use tokio_modbus::{client::tcp::connect_slave, Slave};
#[derive(Parser)]
struct Args {
addr: SocketAddr,
device_id: u8,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
let mut client = AsyncClient::new(
connect_slave(args.addr, Slave(args.device_id)).await?,
Config::default(),
).await?;
let m1: Model1 = client.read_model().await?;
println!("Manufacturer: {}", m1.mn);
println!("Model: {}", m1.md);
println!("Version: {}", m1.vr.as_deref().unwrap_or("(unspecified)"));
println!("Serial Number: {}", m1.sn);
println!(
"Supported models: {}",
client
.models
.supported_model_ids()
.iter()
.map(|id| id.to_string())
.join(", ")
);
loop {
let m103: Model103 = client.read_model().await?;
let w = m103.w as f32 * 10f32.powf(m103.w_sf.into());
let wh = m103.wh as f32 * 10f32.powf(m103.wh_sf.into());
println!("{:12.3} kWh {:9.3} kW", wh / 1000.0, w / 1000.0,);
sleep(Duration::from_secs(1)).await;
}
}
```
## FAQ
How does this crate differ from crates like `tokio-sunspec`, `sunspec-models`, `sunspec_rs`?
- This crate generates all code using Rust code via the official
[SunSpec models repository](https://github.com/sunspec/models/)
with a code generator that was written in Rust, too.
- All generated models are plain Rust structs. A single Modbus call
can return the complete data for a model rather than having to fetch
points individually.
- All public types are documented. Even the generated models.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0)>
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT)>
at your option.