Digital Inputs and Outputs

Digital inputs and outputs are used for communication between a robot and external devices or sensors by digital signals. Unlike analog signals, digital signals are binary: they can only be in one of two states, PinLevel::High or PinLevel::Low. Digital inputs can only be read, while digital outputs can be read and written to.

Depending on the specific robot, a digital input or output may represent a physical pin on the robot cabinet or an abstract bit on a fieldbus.

Note

DigitalInputs and DigitalOutputs are not implemented by FanucRobotArm or FanucRobotArmView.

Reading and Writing Digital Inputs and Outputs

A digital output is represented by a DigitalOutput handle and a digital input by a DigitalInput handle. They are retrieved with digital_output() and digital_input(), which take the id of the pin as a PinId. The first pin has id 1; max_output_id() and max_input_id() return the highest valid id.

A handle is live: every read goes to the robot control. A digital output is set high with set_high() and low with set_low(); write(level) does either. Each write returns once the robot control has confirmed the new level, at most after the robot’s command timeout. The current level of an output or input is read with level(), or checked with is_high() and is_low().

For a quick interaction, an output can be read, set, and cleared inline:

let output_id = PinId(1);

robot.digital_output(output_id)?.set_high()?;

let level = robot.digital_output(output_id)?.level()?;
info!("Output {output_id} is {level}");

robot.digital_output(output_id)?.set_low()?;

Digital inputs can only be read but are used in the same fashion.

If one input or output is used multiple times, keep the handle in a variable instead of retrieving it repeatedly. A handle borrows the robot, so the robot cannot be disconnected or dropped while a handle is still in use:

let output = robot.digital_output(PinId(1))?;

output.set_high()?;
info!("Output {} is {}", output.id(), output.level()?);
output.set_low()?;

Waiting for an Input

Handles can wait for a level. wait_until() polls the robot control until the pin reads the given level and returns RobotArmError::Timeout if it does not happen in time:

let valve = robot.digital_output(PinId(2))?;
let workpiece_present = robot.digital_input(PinId(3))?;

valve.write(PinLevel::High)?;
// Polls at the library interval; returns `Timeout` if the sensor does not
// report the level in time.
match workpiece_present.wait_until(PinLevel::High, Duration::from_millis(500)) {
    Ok(()) => info!("Workpiece detected."),
    Err(RobotArmError::Timeout { .. }) => info!("No workpiece within 500 ms."),
    Err(e) => return Err(e),
}
valve.write(PinLevel::Low)?;

Writing Helpers Against the Pin Traits

The capabilities of a pin are traits: DigitalPin for anything readable (id(), level(), is_high(), is_low(), wait_until()) and WritableDigitalPin for anything writable (set_high(), set_low(), write()). Helper code written against them accepts inputs and outputs alike:

fn log_pin(pin: &impl DigitalPin) -> Result<()> {
    info!("Pin {} is {}", pin.id(), pin.level()?);
    Ok(())
}
log_pin(&robot.digital_input(PinId(1))?)?;
log_pin(&robot.digital_output(PinId(1))?)?;

Using Input and Output Groups

While single inputs and outputs are often sufficient for simple logic, most peripherals require setting more than one output or reading more than one input together. DigitalInputGroup and DigitalOutputGroup bundle multiple pins in one object. They are retrieved with digital_input_group() and digital_output_group(), which take a slice of pin ids.

The order of the ids is the order of the group. It defines the order of the values returned by read_values() and the bit order of read_int(), where the first pin is bit 0, as well as their write_* counterparts. An id may not appear twice.

The current values of the whole group can be read in different representations with read_map(), read_values(), and read_int(). Likewise, all outputs of a group can be set from the same representations with write_map(), write_values(), and write_int(). write_map() takes each id at most once.

The following example shows how conveyors connected to the digital outputs 1, 2, and 4 could be switched on and off:

let conveyors = robot.digital_output_group(&[PinId(1), PinId(2), PinId(4)])?;

conveyors.write_map([
    (PinId(1), PinLevel::High),
    (PinId(2), PinLevel::Low),
    (PinId(4), PinLevel::High),
])?;
info!("Conveyors state: {:?}", conveyors.read_values()?);

// Write all pins at once as an integer, first pin in bit 0. Zero sets
// every output of the group low.
conveyors.write_int(0)?;

Method

Representation

read_map() / write_map()

Pairs of pin id and level; write_map takes any iterator of (PinId, PinLevel).

read_values() / write_values()

Vec<PinLevel> and &[PinLevel] in the order of the group’s ids.

read_int() / write_int()

A u64, first pin as bit 0, high as 1; groups of over 64 pins are rejected.

Please note that the control of a group is not exclusive: single inputs and outputs can still be used as usual, and pins can be shared between multiple groups. The values of a group are read in one snapshot, but written one pin after the other, each confirmed by the control before the next; if a write fails, the pins written before it keep their new state.

Full Example

Trait Definitions