Controller inputs and outputs

The controller carries twenty inputs and outputs, on top of the six of the tool connector documented in Tool inputs and outputs. The IOs of the controller are prefixed with c, and the robot user manual documents their wiring on the connector: pin assignment, wire colors, ground and supply.

Controller IO labels

Labels

What they are

cdi1 to cdi8

Digital inputs, read only.

cdo1 to cdo8

Digital outputs.

cai1, cai2

Analog inputs, read only, in volts.

cao1, cao2

Analog outputs, in volts.

Reading and writing

get_all() returns the inputs and outputs of the controller and of the tool at once, keyed by label:

1 from pyniryo_nate import Nate
2 from pyniryo_nate.models import IOLabel
3
4 nate = Nate(hostname="10.10.10.10", login=("user", "secret"))
5
6 states = nate.io.get_all()
7 print(states.digital_inputs[IOLabel.CDI3].value)
8
9 nate.io.set_digital_output(IOLabel.CDO1, True)

Outputs that must change together are written in one call, so that the robot never passes through an intermediate combination:

1 from pyniryo_nate.models import DigitalIO, IOLabel
2
3 nate.io.update_states([
4     DigitalIO(id=IOLabel.CDO1, value=True),
5     DigitalIO(id=IOLabel.CDO2, value=False),
6 ])

Analog outputs

cao1 and cao2 are driven in volts by set_analog_output(). The tool connector carries no analog output, so these two are the only ones a program can write.

1 nate.io.set_analog_output(IOLabel.CAO1, 4.5)

The range of a channel belongs to the robot, which refuses a write outside it with ClientError. The message of the exception names the channel, the value that was sent and the range the robot serves:

1 from pyniryo_nate.exceptions import ClientError
2
3 try:
4     nate.io.set_analog_output(IOLabel.CAO1, 42.0)
5 except ClientError as error:
6     print(error)

Warning

Reading an analog input gives back what is measured, out of range values included. The robot checks what a program writes, not what the world does to its inputs.

Waiting on an input

wait_for_digital_io() blocks until an input holds a value. A program waiting on a sensor, a button or a handshake from a machine needs nothing more:

1 nate.io.wait_for_digital_io(IOLabel.CDI1, True, timeout=30)

The current state is read once the subscription is in place, so an input that already holds the value returns at once rather than waiting for a change that will not come, and a change happening meanwhile is not missed. A label the robot serves no digital IO under is refused instead of waited on. Without a timeout the call waits forever; with one it raises TimeoutError.

on_digital_io() and on_analog_io() report a single IO as it changes, without polling and without blocking:

1 unsubscribe = nate.io.on_digital_io(IOLabel.CDI1, lambda io: print(io.value))
2
3 # later, to stop receiving the changes
4 unsubscribe()

Labels this client does not know

IOLabel holds the labels that existed when this version was released. A robot may serve more. Those are reported with their raw label, and the rest of the answer is served as usual:

1 states = nate.io.get_all()
2
3 print(states.digital_inputs['cdi9'].value)
4 nate.io.set_digital_output('cdo9', True)

An unknown label is sent as it is and the robot decides, so a program keeps working against hardware released after the client that drives it. A label the robot does not serve is refused with ClientError.

When the robot serves no IO

A read and a write both raise ServerError with a status code of 503 while the chain that carries the inputs and outputs is not running. A robot that is not ready is told apart from a robot that has no input and no output, which an empty answer would hide.

1 from pyniryo_nate.exceptions import ServerError
2
3 try:
4     states = nate.io.get_all()
5 except ServerError as error:
6     if error.status_code == 503:
7         print(f"the robot is not serving its IOs yet: {error.reason}")
8     else:
9         raise