Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Tutorial: OSGi Remote Services for Raspberry Pi GPIO
Contents
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
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.
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
and the LED that's connected to the Raspberry Pi lights up
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
Getting Started with ECF's OSGi Remote Services Implementation