How to read the status of digital outputs?

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). 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 ) 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.

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

I always recommend these tutorials for RTDE beginners: Universal Robot RTDE tutorial #1 (Where to start the journey)

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, 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.

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:


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
And you can read this value directly with URScript:

Since you can already make your URCap write URScript, I would recommend to try this approach. Also in URScript you can create a MODBUS signals (see modbus_add_signal).

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

The screenshots from my answer are from URSim

You can actually access the physical IO directly from Java.

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

private final IOHelper ioHelper;

And assign it a value in the constructor:

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

Now you can just directly declare DigitalIO:

DigitalIO myOutput;

And assign it to the actual IO using the IOHelper:

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

1 Like

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.

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.

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

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();
}

1 Like