# How to read the status of digital outputs?

**URL:** <https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637>\
**Category:** Technical Questions\
**Created:** [October 23, 2024, 8:33am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637 "2024-10-23T08:33:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![j.korte](https://avatars.discourse-cdn.com/v4/letter/j/a3d4f5/32.png) [@j.korte](https://forum.universal-robots.com/u/j.korte)\
**Post date:** [October 23, 2024, 8:33am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/1 "2024-10-23T08:33:43Z")

</div>

Hi,  
I am trying to write a URCap that can read and display the status of digital outputs. I used the mytoolbar example, to create a toolbar that displays the status of the outputs.  
For the beginning I just displayed JLabels and JPanels to show status lights in gray (off) and blue (on). For now I control the status of all status lights via a single boolean variable. Later I plan to use an array to display diffrent states, but for now I just want to try one output.

Then I tried to orient the Script part of the URCap by the LightUp example from the “my first urcap” article (\> [Universal Robots - URCap - My First URCap](https://www.universal-robots.com/articles/ur/urplus-resources/urcap-my-first-urcap/)). There I saw that the “set\_standard\_digital\_out()” is used and I thoght that than there must be an “get\_standard\_digital\_out()”. A look in the scriptManual Page 87 (\> [Universal Robots - Script manual - e-Series - SW 5.11](https://www.universal-robots.com/download/manuals-e-seriesur20ur30/script/script-manual-e-series-sw-511/) ) showed that there is exactly this command and it returns a boolean, but I had no idea how to save the return value in a variable.

Then I found out that I get no return value when implemented this way. More research showed me that I’d need to use rtde but I have no Idea how to implement it.

Thanks for your help.

 ![program](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/b/b114377b4017c05ecddb567f6ddd4b6bf0a1c475.png)

Edit: I do not have a real robot. I use URSim to test my URCaps.

---

<div class="post-metadata">

**Author:** ![cags](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.universal-robots.com/cags/32/6304_2.png) [@cags](https://forum.universal-robots.com/u/cags)\
**Post date:** [October 23, 2024, 10:03am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/2 "2024-10-23T10:03:07Z")

</div>

I always recommend these tutorials for RTDE beginners: [Universal Robot RTDE tutorial #1 (Where to start the journey)](https://www.youtube.com/watch?v=N2nh3iG7kvo&list=PLnJ9fSRnDN3B1wEuxQY4thTWyGoT2N0yd)

Basically you need to connect to the port 30004 and retrieve the info from it with the help from rtde library.  
The big issue I see here is that the RTDE library is [only available in Python](https://github.com/UniversalRobots/RTDE_Python_Client_Library), meaning you would have to either convert the library to Java manually or use the Python code and wrap it in the URCap [similar to this example](https://github.com/UniversalRobots/MyDaemonSwing).

_ **Another approach** _  
However, I think this might be easier if you use the controller internal [MODBUS Server in address 127.0.0.1, address 1](https://www.universal-robots.com/articles/ur/interface-communication/modbus-server/):

 ![image](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/5/5abbd2b46ea0e4da03fa58b1b19ef654290da463.png)  
See here that the register in port 1 is the outputs (the standard IO), it is an int number, where the bit 0 is the output 0, bit 1 is output 1 and so on:  
 ![image](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/9/9a17b28a9d5e880b4252cbabee3654296f8ea208.png)  
 ![image](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/c/cce4c119f35550126086a4cdb41824b58013ecfe.png)  
And you can read this value directly with URScript:  
 ![image](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/c/c6cbe1322d76a41095d1af2b071ae13d7e7742a6.png)

Since you can already make your URCap write URScript, I would recommend to try this approach. Also in [URScript](https://s3-eu-west-1.amazonaws.com/ur-support-site/115824/scriptManual_SW5.11.pdf) you can create a MODBUS signals (see ` modbus_add_signal`).

---

<div class="post-metadata">

**Author:** ![j.korte](https://avatars.discourse-cdn.com/v4/letter/j/a3d4f5/32.png) [@j.korte](https://forum.universal-robots.com/u/j.korte)\
**Post date:** [October 23, 2024, 10:27am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/3 "2024-10-23T10:27:19Z")

</div>

Thanks for the fast answer. I’ll try to us your input and share my progress.

I just saw that i forgot to say that I do not have a real robot and use URSim to this moment. Do not know if this is Helpful

---

<div class="post-metadata">

**Author:** ![cags](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.universal-robots.com/cags/32/6304_2.png) [@cags](https://forum.universal-robots.com/u/cags)\
**Post date:** [October 23, 2024, 10:30am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/4 "2024-10-23T10:30:56Z")

</div>

The screenshots from my answer are from URSim

---

<div class="post-metadata">

**Author:** ![eric.feldmann](https://avatars.discourse-cdn.com/v4/letter/e/94ad74/32.png) [@eric.feldmann](https://forum.universal-robots.com/u/eric.feldmann)\
**Post date:** [October 23, 2024, 8:52pm UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/5 "2024-10-23T20:52:23Z")

</div>

You can actually access the physical IO directly from Java.

```auto
import java.util.Collection;
import java.util.Iterator;

import com.ur.urcap.api.domain.io.DigitalIO;
import com.ur.urcap.api.domain.io.IO;
import com.ur.urcap.api.domain.io.IOModel;
import com.ur.urcap.api.domain.io.ModbusIO;

/*
 * A library class for getting the IO on the robot
 */

public class IOHelper {

	private final IOModel ioModel;

	public IOHelper(final IOModel ioModel) {
		this.ioModel = ioModel;
	}

	public DigitalIO[] getDigitalInputs() {
		final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
		ioCollection.removeIf(n -> !n.isInput());
		final int inputSize = ioCollection.size();
		final Iterator<DigitalIO> itr = ioCollection.iterator();
		final DigitalIO[] inputList = new DigitalIO[inputSize];

		for (int i = 0; i < inputSize; i++) {
			inputList[i] = itr.next();
		}
		return inputList;
	}
	
	
	
	public DigitalIO[] getDigitalOutputs() {
		final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
		ioCollection.removeIf(DigitalIO::isInput);
		final int outputSize = ioCollection.size();
		final Iterator<DigitalIO> itr = ioCollection.iterator();
		final DigitalIO[] outputList = new DigitalIO[outputSize];

		for (int i = 0; i < outputSize; i++) {
			outputList[i] = itr.next();
		}
		return outputList;
	}

	public ModbusIO[] getModBusInputs() {
		final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
		ioCollection.removeIf(n -> !n.isInput());
		final int inputSize = ioCollection.size();
		final Iterator<ModbusIO> itr = ioCollection.iterator();
		final ModbusIO[] inputList = new ModbusIO[inputSize];

		for (int i = 0; i < inputSize; i++) {
			inputList[i] = itr.next();
		}
		return inputList;
	}

	public ModbusIO[] getModBusOutputs() {
		final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
		ioCollection.removeIf(ModbusIO::isInput);
		final int outputSize = ioCollection.size();
		final Iterator<ModbusIO> itr = ioCollection.iterator();
		final ModbusIO[] outputList = new ModbusIO[outputSize];

		for (int i = 0; i < outputSize; i++) {
			outputList[i] = itr.next();
		}
		return outputList;
	}

	public DigitalIO getSpecificDigitalIO(final String defaultName) {
		final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
		final int ioCount = ioCollection.size();

		if (ioCount > 0) {
			final Iterator<DigitalIO> ioItr = ioCollection.iterator();
			while (ioItr.hasNext()) {
				final DigitalIO thisIO = ioItr.next();
				final String thisDefaultName = thisIO.getDefaultName();
				if (thisDefaultName.equals(defaultName)) {
					return thisIO;
				}
			}
		}
		return null;
	}
	
	public IO getSpecificGPIO(final String defaultName) {
		final Collection<IO> ioCollection = ioModel.getIOs();
		final int ioCount = ioCollection.size();

		if (ioCount > 0) {
			final Iterator<IO> ioItr = ioCollection.iterator();
			while (ioItr.hasNext()) {
				final IO thisIO = ioItr.next();
				final String thisDefaultName = thisIO.getDefaultName();
				if (thisDefaultName.equals(defaultName)) {
					return thisIO;
				}
			}
		}
		return null;
	}
	
	

	public ModbusIO getSpecificModBusIO(final String signalName) {
		final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
		final int ioCount = ioCollection.size();
		if (ioCount > 0) {
			final Iterator<ModbusIO> ioItr = ioCollection.iterator();
			while (ioItr.hasNext()) {
				final ModbusIO thisIO = ioItr.next();
				final String thisSignalAddress = thisIO.getName();
				if (thisSignalAddress.equals(signalName)) {
					return thisIO;
				}
			}
		}
		return null;
	}

}

```

Here’s the contents of the class file called IOHelper. I’m sure I found this on the forum years ago.

Then in your toolbar file, declare an instance of the class

```auto
private final IOHelper ioHelper;

```

And assign it a value in the constructor:

```auto
ioHelper = new IOHelper(context.getAPIProvider().getApplicationAPI().getIOModel());

```

Now you can just directly declare DigitalIO:

```auto
DigitalIO myOutput;

```

And assign it to the actual IO using the IOHelper:

```auto
myOutput= ioHelper.getSpecificDigitalIO("digital_out[0]");

```

This DigitalIO object has .getValue() and .setValue() methods that you can use.

For a full list of the IO names that you can pass to the IO helper, you can see this link: [How to obtain a list of ALL the IOs on the robot using the URCap Java API - #3 by fujikit](https://forum.universal-robots.com/t/how-to-obtain-a-list-of-all-the-ios-on-the-robot-using-the-urcap-java-api/8075/3)

---

<div class="post-metadata">

**Author:** ![j.korte](https://avatars.discourse-cdn.com/v4/letter/j/a3d4f5/32.png) [@j.korte](https://forum.universal-robots.com/u/j.korte)\
**Post date:** [October 24, 2024, 7:49am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/6 "2024-10-24T07:49:11Z")

</div>

Thank you very much. Your solution worked best for me until now.  
Eclipse shows no error messages.  
I only get error messages when typing  
mvn install -P ursim  
into the terminal.  
It says:  
[ERROR] (use -source 8 or higher to enable lambda expressions)  
[ERROR] /home/ur/workspace/com.ur.urcap.examples.mytoolbar/src/main/java/com/ur/urcap/examples/IOHelper.java:[35,50] method references are not supported in -source 1.6

So I am now trying to replace the lambda expressions.  
Better Ideas are welcome.  
I am keeping you up to date.

---

<div class="post-metadata">

**Author:** ![eric.feldmann](https://avatars.discourse-cdn.com/v4/letter/e/94ad74/32.png) [@eric.feldmann](https://forum.universal-robots.com/u/eric.feldmann)\
**Post date:** [October 24, 2024, 3:07pm UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/7 "2024-10-24T15:07:29Z")

</div>

I would probably just update your source to support lambda expressions. Been a while since I’ve done it, but you should be able to just go to the .pom file and change the 1.6 to 1.8 in the and tags.

 ![image](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/9/97664d79d66c7f2e87ffbc130dd15c45e4da5315.png)

And then you may have to download an updated SDK, again it’s been a while so I forget.

---

<div class="post-metadata">

**Author:** ![j.korte](https://avatars.discourse-cdn.com/v4/letter/j/a3d4f5/32.png) [@j.korte](https://forum.universal-robots.com/u/j.korte)\
**Post date:** [October 25, 2024, 5:12am UTC](https://forum.universal-robots.com/t/how-to-read-the-status-of-digital-outputs/36637/8 "2024-10-25T05:12:54Z")

</div>

I tried that. Did not work. But my URCap is now running as intended.  
Here are the results…  
My contribution class:

> class MyToolbarContribution implements SwingToolbarContribution {

```
private static final int VERTICAL_SPACE = 10;
private static final int HEADER_FONT_SIZE = 16;

private final ToolbarContext context;

private final IOHelper ioHelper;

private DigitalIO[] myOutputs;

private Timer updateTimer;

private SquarePanel[] squarePanels;

private JLabel demoToolStatus;

MyToolbarContribution(ToolbarContext context) {
	this.context = context;
	ioHelper = new IOHelper(context.getAPIProvider().getApplicationAPI().getIOModel());
	
	myOutputs = new DigitalIO[8];
	for (int i = 0; i < 8; i++) {
		myOutputs[i] = ioHelper.getSpicificDigitalIO("digital_out[" + i + "]");
	}

	updateTimer = new Timer(50, new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			updateOutputStatus();
		}
	});
	updateTimer.start();
}

@Override
public void openView() {
	demoToolStatus.setText("<HTML>" + get3rdPartyStatus() + "</HTML>");
	updateTimer.start(); // start timer 
}

@Override
public void closeView() {
	updateTimer.stop(); // close timer
}

private void updateOutputStatus() {
	for (int i = 0; i < myOutputs.length; i++) {
		if (myOutputs[i] != null) {
			boolean diOut = myOutputs[i].getValue();
			updateUI(i, diOut);
		}
	}
}

public void buildUI(JPanel jPanel) {
	jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.Y_AXIS));

	jPanel.add(createHeader());
	jPanel.add(createVerticalSpace());
	jPanel.add(createInfo());
}

private Box createHeader() {
	Box headerBox = Box.createHorizontalBox();
	headerBox.setAlignmentX(Component.CENTER_ALIGNMENT);

	JLabel header = new JLabel("Signal Information Center");
	header.setFont(header.getFont().deriveFont(Font.BOLD, HEADER_FONT_SIZE));
	headerBox.add(header);
	return headerBox;
}

private Box createInfo() {
	Box infoBox = Box.createVerticalBox();
	infoBox.setAlignmentX(Component.CENTER_ALIGNMENT);

	squarePanels = new SquarePanel[8];

	JPanel[] panelM = new JPanel[8];

	for (int i = 0; i < 8; i++) {
		panelM[i] = new JPanel();
		panelM[i].setLayout(new BoxLayout(panelM[i], BoxLayout.X_AXIS));

		JLabel label = new JLabel("Output " + i);

		squarePanels[i] = new SquarePanel(Color.LIGHT_GRAY);

		panelM[i].add(label);
		panelM[i].add(squarePanels[i]);

		infoBox.add(panelM[i]);
	}

	demoToolStatus = new JLabel();
	demoToolStatus.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
	infoBox.add(demoToolStatus);
	return infoBox;
}

private void updateUI(int index, boolean diOut) {
	if (diOut) {
		squarePanels[index].setSquareColor(Color.BLUE);
	} else {
		squarePanels[index].setSquareColor(Color.LIGHT_GRAY);
	}
}

private Component createVerticalSpace() {
	return Box.createRigidArea(new Dimension(0, VERTICAL_SPACE));
}

private String get3rdPartyStatus() {
	Date now = new Date();
	return String.format("Read at %tF %tT.", now, now);
}

```

}

My updated IOHelper class looks like this;

> public class IOHelper {

```
private final IOModel ioModel;

public IOHelper(final IOModel ioModel) {
	this.ioModel = ioModel;
}

public DigitalIO[] getDigitalInputs() {
	final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
	ioCollection.removeIf(new Predicate<DigitalIO>() {
		@Override
		public boolean test(DigitalIO n) {
			return !n.isInput();
		}
	});
	final int inputSize = ioCollection.size();
	final Iterator<DigitalIO> itr = ioCollection.iterator();
	final DigitalIO[] inputList = new DigitalIO[inputSize];
	
	for (int i = 0 ; i < inputSize; i++) {
		inputList[i] = itr.next();
	}
	return inputList;
}

public DigitalIO[] getDigitalOutputs() {
	final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
	ioCollection.removeIf(new Predicate<DigitalIO>() {
		@Override
		public boolean test(DigitalIO io) {
			return io.isInput();
		}
	});
	final int outputSize = ioCollection.size();
	final Iterator<DigitalIO> itr = ioCollection.iterator();
	final DigitalIO[] outputList = new DigitalIO[outputSize];
	
	for (int i = 0; i < outputSize; i++) {
		outputList[i] = itr.next();
	}
	return outputList;
}

public ModbusIO[] getModBusInputs() {
	final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
	ioCollection.removeIf(new Predicate<ModbusIO>() {
		@Override
		public boolean test(ModbusIO n) {
			return !n.isInput();
		}
	});
	final int inputSize = ioCollection.size();
	final Iterator<ModbusIO> itr = ioCollection.iterator();
	final ModbusIO[] inputList = new ModbusIO[inputSize];
	
	for (int i = 0; i < inputSize; i++) {
		inputList[i] = itr.next();
	}
	return inputList;
}

public ModbusIO[] getModBusOutputs() {
	final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
	ioCollection.removeIf(new Predicate<ModbusIO>() {
		@Override
		public boolean test(ModbusIO io) {
			return io.isInput();
		}
	});
	final int outputSize = ioCollection.size();
	final Iterator<ModbusIO> itr = ioCollection.iterator();
	final ModbusIO[] outputList = new ModbusIO[outputSize];
	
	for (int i = 0; i < outputSize; i++) {
		outputList[i] = itr.next();
	}
	return outputList;
}

public DigitalIO getSpicificDigitalIO(final String defaultName) {
	final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
	final int ioCount = ioCollection.size();
	
	if (ioCount > 0) {
		final Iterator<DigitalIO> ioItr = ioCollection.iterator();
		while (ioItr.hasNext()) {
			final DigitalIO thisIO = ioItr.next();
			final String thisDefaultName = thisIO.getDefaultName();
			if (thisDefaultName.contentEquals(defaultName)) {
				return thisIO;
			}
		}
	}
	return null;
}

public IO getSpecificGPIO(final String defaultName) {
	final Collection<IO> ioCollection = ioModel.getIOs();
	final int ioCount = ioCollection.size();
	
	if (ioCount > 0) {
		final Iterator<IO> ioItr = ioCollection.iterator();
		while (ioItr.hasNext()) {
			final IO thisIO = ioItr.next();
			final String thisDefaultName = thisIO.getDefaultName();
			if (thisDefaultName.contentEquals(defaultName)) {
				return thisIO;
			}
		}
	}
	return null;
}

public ModbusIO getspecificModBusIO(final String signalName) {
	final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
	final int ioCount = ioCollection.size();
	if (ioCount > 0) {
		final Iterator<ModbusIO> ioItr = ioCollection.iterator();
		while (ioItr.hasNext()) {
			final ModbusIO thisIO = ioItr.next();
			final String thisSignalAddress = thisIO.getName();
			if (thisSignalAddress.contentEquals(signalName)) {
				return thisIO;
			}
		}
	}
	return null;
}

```

}

I also needed to update my SquarePanel class to update the color of the squares:

```
private Color squareColor;

public SquarePanel(Color color) {
	this.squareColor = color;
	this.setPreferredSize(new Dimension(20, 20));
}

@Override
protected void paintComponent(Graphics g) {
	super.paintComponent(g);
	g.setColor(squareColor);
	g.fillRect(30, 20, 15, 15);
}

public void setSquareColor(Color newColor) {
	this.squareColor = newColor;
	repaint();
}

```

 ![image](https://us1.discourse-cdn.com/flex020/uploads/universal_robots/original/2X/1/1ba7c9989893e9e527290bb460119823280b840e.png)
