ruuvi-decoders

Crates.ioruuvi-decoders
lib.rsruuvi-decoders
version1.0.0
created_at2025-10-13 14:45:01.210388+00
updated_at2025-11-02 21:24:36.089018+00
descriptionRuuvi BLE advertisement decoders for Data Formats v5, v6, and E1
homepage
repositoryhttps://github.com/viljami/ruuvi-decoders
max_upload_size
id1880615
size105,253
Viljami Peltola (viljami)

documentation

README

Ruuvi Decoders ๐Ÿฆ€

Crates.io Documentation License: MIT Build Status

High-performance Rust library for decoding Ruuvi sensor BLE advertisements. Supports all major Ruuvi data formats with comprehensive validation and type safety.

Features

  • ๐Ÿš€ High Performance: Optimized for minimal latency
  • ๐Ÿ”’ Type Safe: Leverages Rust's type system for data integrity
  • ๐Ÿ“Š Complete Coverage: Supports Data Formats v5, v6, and E1
  • ๐Ÿงช Thoroughly Tested: All official test vectors pass
  • ๐Ÿ”ง Easy Integration: Simple API with comprehensive error handling

Supported Formats

Format Status Description
v5 (RAWv2) โœ… Complete Temperature, humidity, pressure, acceleration, battery
v6 โœ… Complete Adds PM2.5, CO2, VOC, NOX, luminosity
E1 โœ… Complete Extended format with PM1.0/2.5/4.0/10.0

Quick Start

Add this to your Cargo.toml:

[dependencies]
ruuvi-decoders = "0.2"

Basic Usage

use ruuvi_decoders::{decode, extract_ruuvi_from_ble};

// From a full BLE advertisement
let ble_data = "02010603031691FF990405159F7C025A8BC4A53C00FB00000000E7FEE7FE00E7FE";
let ruuvi_hex = extract_ruuvi_from_ble(ble_data).unwrap();
let decoded = decode(&ruuvi_hex).unwrap();

match decoded {
    ruuvi_decoders::RuuviData::V5(data) => {
        println!("Temperature: {}ยฐC", data.temperature.unwrap());
        println!("Humidity: {}%", data.humidity.unwrap());
        println!("Pressure: {} Pa", data.pressure.unwrap());
        println!("MAC: {}", data.mac_address);
    },
    // Handle v6 and E1 formats...
    _ => println!("Other format"),
}

Direct Hex Decoding

use ruuvi_decoders::decode;

// Direct Ruuvi payload (without BLE wrapper)
let hex_data = "0512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F";
let result = decode(hex_data).unwrap();

if let ruuvi_decoders::RuuviData::V5(data) = result {
    assert_eq!(data.temperature, Some(24.3));
    assert_eq!(data.humidity, Some(53.49));
    assert_eq!(data.pressure, Some(100044.0));
}

Data Format v5 (RAWv2)

The most common format used by RuuviTag sensors:

use ruuvi_decoders::v5::decode;

let bytes = hex::decode("0512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F").unwrap();
let data = decode(&bytes).unwrap();

println!("Sensor Data:");
println!("  MAC: {}", data.mac_address);
println!("  Temperature: {:.2}ยฐC", data.temperature.unwrap_or(0.0));
println!("  Humidity: {:.2}%", data.humidity.unwrap_or(0.0));
println!("  Pressure: {:.0} Pa", data.pressure.unwrap_or(0.0));
println!("  Battery: {} mV", data.battery_voltage.unwrap_or(0));
println!("  TX Power: {} dBm", data.tx_power.unwrap_or(0));

Error Handling

The library provides comprehensive error handling:

use ruuvi_decoders::{decode, DecodeError};

match decode("invalid_hex") {
    Ok(data) => println!("Decoded: {:?}", data),
    Err(DecodeError::InvalidHex(msg)) => eprintln!("Invalid hex: {}", msg),
    Err(DecodeError::UnsupportedFormat(format)) => {
        eprintln!("Unsupported format: 0x{:02X}", format)
    },
    Err(DecodeError::InvalidLength(msg)) => eprintln!("Wrong length: {}", msg),
    Err(e) => eprintln!("Other error: {}", e),
}

Integration with Serde

All data structures support serialization:

use ruuvi_decoders::decode;

let hex_data = "0512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F";
let decoded = decode(hex_data).unwrap();

// Serialize to JSON
let json = serde_json::to_string(&decoded).unwrap();
println!("JSON: {}", json);

// Deserialize from JSON
let restored: ruuvi_decoders::RuuviData = serde_json::from_str(&json).unwrap();

Validation and Invalid Values

The library properly handles invalid/unavailable sensor readings:

use ruuvi_decoders::v5::decode;

// Test with invalid values (from official test vectors)
let invalid_data = hex::decode("058000FFFFFFFF800080008000FFFFFFFFFFFFFFFFFFFFFF").unwrap();
let result = decode(&invalid_data).unwrap();

// All sensor readings will be None for invalid data
assert_eq!(result.temperature, None);
assert_eq!(result.humidity, None);
assert_eq!(result.pressure, None);
assert_eq!(result.mac_address, "invalid");

Performance

Ruuvi Decoders is optimized for high-throughput scenarios:

  • Decoding: ~0.8ฮผs per v5 message on modern hardware
  • Memory: Zero heap allocations in decode path
  • Throughput: >1M messages/second/core

Run benchmarks:

cargo bench

Examples

See the examples/ directory for complete examples:

Run an example:

cargo run --example basic_usage

Specification Compliance

This library implements the official Ruuvi specifications:

All test vectors from the official documentation are included and pass.

Development

Building

git clone https://github.com/ruuvi/ruuvi-decoders
cd ruuvi-decoders
cargo build

Testing

# Run all tests
cargo test

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Related Projects

Commit count: 0

cargo fmt