nanonis-rs 0.1.0

Rust client library for Nanonis SPM system control via TCP
Documentation

nanonis-rs

Crates.io Documentation License: MIT

A Rust client library for communicating with Nanonis SPM (Scanning Probe Microscopy) systems via the Nanonis TCP protocol. This library provides a type-safe, high-level interface for controlling scanning probe microscopes and reading measurement data.

Features

  • Type-safe API: Strongly-typed Rust interface to Nanonis commands
  • Comprehensive coverage: Support for motors, scanning, spectroscopy, oscilloscopes, and more
  • Error handling: Robust error types with detailed context
  • TCPLogger streaming: Continuous data acquisition support
  • Safety features: Automatic tip withdrawal and motor positioning on client drop
  • Protocol abstraction: Low-level protocol details handled internally

Supported Functionality

  • Bias control: Set and read bias voltage
  • Signal acquisition: Read signal values and metadata
  • Motor control: Precise control of XYZ motors and FolMe positioning
  • Z-controller: Z-feedback loop control and monitoring
  • Scanning: Scan control and data acquisition
  • Auto-approach: Automated tip approach functionality
  • Oscilloscopes: Single-trigger, dual-trigger, and high-resolution modes
  • Spectroscopy: Z-spectroscopy and bias sweeps
  • Tip management: Tip shaping and recovery procedures
  • PLL control: Phase-locked loop operations
  • TCPLogger: Real-time data streaming

Quick Start

Add this to your Cargo.toml:

[dependencies]
nanonis-rs = "0.0.3"

Basic Example

use nanonis_rs::{NanonisClient, NanonisError};

fn main() -> Result<(), NanonisError> {
    // Connect to Nanonis system
    let mut client = NanonisClient::new("192.168.1.100", 6501)?;

    // Get current bias voltage
    let bias = client.bias_get()?;
    println!("Current bias: {} V", bias);

    // Set new bias voltage
    client.bias_set(0.5)?;

    // Read signal names
    let signal_names = client.signal_names_get()?;
    println!("Available signals: {:?}", signal_names);

    // Read specific signal values (indices 0, 1, 2)
    let values = client.signals_vals_get(vec![0, 1, 2], true)?;
    println!("Signal values: {:?}", values);

    Ok(())
}

Motor Control Example

use nanonis_rs::{NanonisClient, NanonisError, MotorDirection, MotorGroup};
use std::time::Duration;

fn main() -> Result<(), NanonisError> {
    let mut client = NanonisClient::new("192.168.1.100", 6501)?;

    // Move motor with specified steps
    client.motor_start_move(
        MotorDirection::ZPlus,
        1000u16,  // steps
        MotorGroup::Group1,
        false  // non-blocking
    )?;

    // Wait for movement to complete
    std::thread::sleep(Duration::from_millis(500));

    Ok(())
}

TCPLogger Streaming Example

use nanonis_rs::TCPLoggerStream;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut stream = TCPLoggerStream::connect("192.168.1.100:6502")?;

    // Read streaming data
    loop {
        let data = stream.read_data()?;
        println!("Timestamp: {}, Channels: {}",
                 data.timestamp, data.channels.len());

        // Process data...
    }
}

Architecture

The library is organized into several layers:

  • Protocol layer (protocol.rs): Low-level TCP message encoding/decoding
  • Type system (types.rs): Protocol value types and domain-specific types
  • Client layer (client/): High-level API organized by functionality
  • Error handling (error.rs): Comprehensive error types with context

All communication with Nanonis follows a request-response pattern with strongly-typed inputs and outputs.

Connection Configuration

Create clients using the builder pattern for advanced configuration:

use nanonis_rs::NanonisClient;
use std::time::Duration;

let client = NanonisClient::builder()
    .address("192.168.1.100")
    .port(6501)
    .connect_timeout(Duration::from_secs(10))
    .read_timeout(Duration::from_secs(30))
    .build()?;

Or use the simpler constructor:

let client = NanonisClient::new("192.168.1.100", 6501)?;

Safety Features

The client includes optional safety features:

  • Timeout protection: All operations have configurable timeouts
  • Optional auto-withdrawal: Enable safe_tip_on_drop to automatically withdraw the tip and move motors to a safe position when the client is dropped
// Enable automatic tip safety on drop (opt-in)
let client = NanonisClient::builder()
    .address("192.168.1.100")
    .port(6501)
    .safe_tip_on_drop(true)  // Will withdraw tip when client drops
    .build()?;

Error Handling

The library uses typed errors via NanonisError:

match client.bias_get() {
    Ok(bias) => println!("Bias: {} V", bias),
    Err(NanonisError::Timeout) => eprintln!("Connection timeout"),
    Err(NanonisError::ServerError { code, message }) => {
        eprintln!("Server error {}: {}", code, message)
    }
    Err(e) => eprintln!("Error: {}", e),
}

Documentation

Full API documentation is available on docs.rs. The documentation includes detailed examples for each command.

Requirements

  • Rust 2021 edition or later
  • Nanonis system with TCP interface enabled
  • Network access to Nanonis controller

Development

This project uses Nix flakes for reproducible development environments:

# Enter development shell
nix develop

# Build the library
cargo build

# Run tests (requires Nanonis connection)
cargo test

# Build documentation
cargo doc --open

License

Licensed under the MIT License. See LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues on GitHub.

Acknowledgments

This library implements the Nanonis TCP protocol as documented by SPECS Zurich GmbH. Nanonis is a registered trademark of SPECS Zurich GmbH.