Jump to: navigation, search

Tutorial: OSGi Remote Services for Raspberry Pi GPIO


Introduction

In a previous tutorial we showed how to abstract the Raspberry Pi's GPIO pins as a very simple OSGi Services. This tutorial will show how to export IGPIOPinOutput service instances for remote access via a simple and standard mechanism known as OSGi Remote Services. We also show how to create and use a very simple Eclipse-based user interface as a client for controlling devices connected to the Pi's GPIO.

See the previous tutorial for the declaration and description of the GPIO Pin Output service interface.

Exporting the IGPIOPinOutput Service

To make the control of GPIO pin 0 available for remote access, all that's necessary is to provide some service properties when registering the instance of the IGPIOPinOutput instance. The service properties are standardized by the OSGi Remote Services chapter 100 in the OSGi Enterprise specification.

Here is the code to export the IGPIOPinOutput service

Map<String, Object> pinProps = new HashMap<String, Object>();
pinProps.put("service.exported.interfaces", "*");
pinProps.put("service.exported.configs", "ecf.generic.server");
pinProps.put("ecf.generic.server.port", "3288");
pinProps.put("ecf.generic.server.hostname",InetAddress.getLocalHost().getHostAddress());
pinProps.put("ecf.exported.async.interfaces", "*");
...
 
// register GPIOPin 0 with the above export properties
reg = Pi4jGPIOPinOutput.registerGPIOPinOutput(0, pinProps, context);

See here for the entire source for this example.

With ECF's implementation of OSGi Remote Services present in the target framework, the properties added above will result in the IGPIOPinOutput service being automatically exported as an OSGi Remote service synchronously during the registerService call. Exporting will make it available for subsequent discovery and use by other processes. In the case of the example, the SLP protocol is used for LAN-based network discovery.

Discovering and Importing the IGPIOPinOutput Service

Once exported, remote consumers may discover, import, and then use the IGPIOPinOutput service. To demonstrate this, here is simple Eclipse View present the discovered IGPIOPinOutput remote service instances. The code for this Eclipse plugin is in the test/bundles/org.eclipse.ecf.raspberrypi.test.gpio.ide project. When run in the Eclipse ide, the empty view (no discovered IGPIOPinOutput services) looks like this

Eclipsenopins.png

After discovering an instance of IGPIOPinOutput remote service via SLP LAN-based discovery the view is automatically updated with the IGPIOPinOutput proxy, which is automatically created by the OSGi Remote Services implementation.

Eclipsepinlow.png

With the discovery of this IGPIOPinOutput remote service, and it's appearance in this view, a simple click on the 'State (click to toggle)' cell results in the pin toggling it's state via a call to IGPIOPinUpdate.setState, resulting in both the Eclipse UI being updated with a different icon

Eclipsepinhigh.png

and the LED that's connected to the Raspberry Pi lights up

Leghigheclipse.png

The same as if we had accessed the IGPIOPinOutput service from the Raspberry Pi directly.

Technical Notes

OSGi Remote Services allows service consumers to ignore the underlying communications transport used for the remoting of the IGPIOPinOutput service. The code implementing the Eclipse view does not access *any* transport-specific mechanisms, allowing both the Raspberry Pi (service host) and the Eclipse view to use whatever provider transport they prefer.

The dynamics of unreliable remote services (i.e. that services come and go over time) is handled extremely well by OSGi services.

ECF provides support for using the Java8 CompletableFuture for asynchronous remote services, eliminating the potential for user interface blocking, without complicated threading code. On the remote service consumer (Eclipse UI in this case), the ECF proxy automatically implements this asynchronous service type


package org.eclipse.ecf.raspberrypi.gpio;
 
import java.util.concurrent.CompletableFuture;
 
public interface IGPIOPinOutputAsync {
 
	public CompletableFuture<Boolean> getStateAsync();
 
	public CompletableFuture<Void> setStateAsync(boolean value);
...
 
}

The full source for this asynchronous service interface is available here.

The Eclipse UI code can then use this asynchronous service to remotely control the GPIO Pin 0 without any blocking operations. The implementation of the IGPIOPinOutputAsync interface is created on the Eclipse client/consumer by the ECF Remote Services proxy creation.

Here is a snippet from the implementation of the GPIO Pin View class, showing the use of the IGPIOPinOutputAsync and support for lambda expressions

public void toggle() {
	// Set waiting to true
	waiting = true;
	boolean newState = !this.state;
	// If we have asynchronous access to service,
	// then use it
	if (pinOutputAsync != null) {
		// Set state asynchronously to newState,
		// and when complete change the UI state
		// and refresh the viewer
		pinOutputAsync.setStateAsync(newState).whenComplete(
				(result, exception) -> {
					this.waiting = false;
					if (viewer != null) {
						// No exception means success
						if (exception == null) {
							// Set UI state to newState
							state = newState;
							asyncRefresh();
						} else
							showCommErrorDialog(exception);
					}
				});
	} else {
		// If we do not have access to async service, then
		// call pinOutput synchronously
		pinOutput.setState(newState);
		state = newState;
	}
}

The entire source for this view class is available here.

More about asynchronous remote services is described in Asynchronous Remote Services.

Related Articles

Tutorial:Raspberry Pi GPIO with OSGi Services

Asynchronous Remote Services

Getting Started with ECF's OSGi Remote Services Implementation

Download ECF Remote Services/RSA Implementation

How to Add Remote Services/RSA to Your Target Platform