profirust 0.6.0

PROFIBUS-DP compatible communication stack in pure Rust
Documentation
use profirust::dp;
use profirust::fdl;
use profirust::phy;

// Bus Parameters
const MASTER_ADDRESS: u8 = 3;
const BUS_DEVICE: &'static str = "/dev/ttyUSB0";
const BAUDRATE: profirust::Baudrate = profirust::Baudrate::B500000;

fn main() -> ! {
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
        .format_timestamp_micros()
        .init();

    log::info!("PROFIBUS Multi-Application Example");

    let mut dp_scanner = dp::scan::DpScanner::new();

    let mut dp_master = dp::DpMaster::new(vec![]);

    // Options generated by `gsdtool` using "wagob757.gsd"
    let options = profirust::dp::PeripheralOptions {
        // "WAGO 750-343" by "WAGO Kontakttechnik GmbH"
        ident_number: 0xb757,

        // Global Parameters:
        //   - DP-Watchdog-Base...............: 10 ms
        //   - Restart on K-Bus Failure.......: POWER ON RESET
        //   - Device Diagnosis...............: enabled
        //   - Process Data Representation....: MOTOROLA (MSB-LSB)
        //   - Response to PROFIBUS DP Failure: Substitude Values are switched
        //   - Response to K-Bus Failure......: PROFIBUS communication stops
        //
        // Selected Modules:
        //   [0] 750-343 No PI Channel
        //   [1] 750-504  4 DO/24 V DC/0.5 A
        //       - Terminal is physically....: plugged
        //       - Substitude Value Channel 1: 0
        //       - Substitude Value Channel 2: 0
        //       - Substitude Value Channel 3: 0
        //       - Substitude Value Channel 4: 0
        //   [2] 750-402  4 DI/24 V DC/3.0 ms
        //       - Terminal is physically: plugged
        //   [3] 750-469  2 AI/TC/OCM
        //       - Terminal is physically: plugged
        //       - Diagnosis Channel 1...: disabled
        //       - Diagnosis Channel 2...: disabled
        user_parameters: Some(&[
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00,
            0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x00, 0x21, 0x02, 0x00, 0x21, 0x01, 0x00,
            0x20, 0x53, 0x01, 0x06,
        ]),
        config: Some(&[0x00, 0x20, 0x10, 0x51]),

        // Set max_tsdr depending on baudrate and assert
        // that a supported baudrate is used.
        max_tsdr: match BAUDRATE {
            profirust::Baudrate::B9600 => 60,
            profirust::Baudrate::B19200 => 60,
            profirust::Baudrate::B93750 => 60,
            profirust::Baudrate::B187500 => 60,
            profirust::Baudrate::B500000 => 100,
            profirust::Baudrate::B1500000 => 150,
            profirust::Baudrate::B3000000 => 250,
            profirust::Baudrate::B6000000 => 350,
            profirust::Baudrate::B12000000 => 550,
            b => panic!("Peripheral \"WAGO 750-343\" does not support baudrate {b:?}!"),
        },

        fail_safe: true,
        ..Default::default()
    };
    let mut buffer_inputs = [0u8; 5];
    let mut buffer_outputs = [0u8; 1];
    let mut buffer_diagnostics = [0u8; 64];
    let handle_io_wago = dp_master.add(
        dp::Peripheral::new(8, options, &mut buffer_inputs[..], &mut buffer_outputs[..])
            .with_diag_buffer(&mut buffer_diagnostics[..]),
    );

    let mut fdl = fdl::FdlActiveStation::new(
        fdl::ParametersBuilder::new(MASTER_ADDRESS, BAUDRATE)
            // We use a rather large T_slot time because USB-RS485 converters
            // can induce large delays at times.
            .slot_bits(4000)
            .max_retry_limit(3)
            // .token_rotation_bits(512)
            .build(),
    );
    // Read more about timing considerations in the SerialPortPhy documentation.
    let sleep_time = std::time::Duration::from_micros(3500);

    log::info!("Connecting to the bus...");
    let mut phy = phy::SerialPortPhy::new(BUS_DEVICE, fdl.parameters().baudrate);

    fdl.set_online();
    dp_master.enter_operate();
    loop {
        fdl.poll_multi(
            profirust::time::Instant::now(),
            &mut phy,
            &mut [&mut dp_master, &mut dp_scanner],
        );

        let dp_events = dp_master.take_last_events();
        if dp_events.cycle_completed {
            let io = dp_master.get_mut(handle_io_wago);
            if io.is_running() {
                log::debug!("WAGO Inputs: {:?}", io.pi_i());
                if io.pi_q_mut()[0] == 0x55 {
                    io.pi_q_mut()[0] = 0xAA;
                } else {
                    io.pi_q_mut()[0] = 0x55;
                }
            }
        }

        let scanner_event = dp_scanner.take_last_event();
        match scanner_event {
            Some(dp::scan::DpScanEvent::PeripheralFound(desc)) => {
                log::info!("Discovered peripheral #{}:", desc.address);
                log::info!("  - Ident: 0x{:04x}", desc.ident);
                log::info!("  - Master: {:?}", desc.master_address);
            }
            Some(dp::scan::DpScanEvent::PeripheralLost(address)) => {
                log::info!("Lost peripheral #{}.", address);
            }
            _ => (),
        }

        std::thread::sleep(sleep_time);
    }
}