Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Tutorial: Raspberry Pi GPIO with OSGi Services"

(GPIO Input)
 
(20 intermediate revisions by the same user not shown)
Line 74: Line 74:
 
==Introduction==
 
==Introduction==
  
This tutorial shows how to access the [http://www.raspberrypi.org/documentation/usage/gpio/ Raspberry Pi GPIO] using OSGi Services. OSGi Services makes the application use of GPIO pins simpler, easier, and more dynamic.
+
This tutorial shows how to use OSGi Services to access the [http://www.raspberrypi.org/documentation/usage/gpio/ Raspberry Pi GPIO].   The use of OSGi Services to use GPIO provides several important advantages:
 +
#The OSGi Service Interface provides a simple and clear yet flexible software abstraction
 +
##Clear Contract Between Service Producer and Service Consumer
 +
##Loose Coupling and High Cohesion
 +
##Flexible yet Simple Abstractions
 +
#OSGi Services provides technical advantages
 +
##Service Dynamics - Services come and go, applications can be notified and adjust
 +
##Service Versioning - Services evolve over time
 +
##Variety of Injection Frameworks - e.g. Declarative Services, Blueprint/Spring
 +
##Network Services - [https://wiki.eclipse.org/ECF#OSGi_Remote_Services ECF Remote Services]
 +
##Service-Level Security
  
==An OSGi Service for GPIO Pin Output==
+
==GPIO Pin Output==
  
 
Each GPIO digital pins have two modes:  output and input.  For output, each pin can be in either the 'HIGH' state, or the 'LOW' state, and the control of the pin state is what we would like to manipulate to give our application behavior.  After connecting a single LED, changing the output on a given pin to HIGH will turn the LED on, while setting to low will turn it off.  See [https://projects.drogon.net/raspberry-pi/gpio-examples/tux-crossing/gpio-examples-1-a-single-led/ this page for a description of how to setup such an LED].   
 
Each GPIO digital pins have two modes:  output and input.  For output, each pin can be in either the 'HIGH' state, or the 'LOW' state, and the control of the pin state is what we would like to manipulate to give our application behavior.  After connecting a single LED, changing the output on a given pin to HIGH will turn the LED on, while setting to low will turn it off.  See [https://projects.drogon.net/raspberry-pi/gpio-examples/tux-crossing/gpio-examples-1-a-single-led/ this page for a description of how to setup such an LED].   
Line 93: Line 103:
 
public void setState(boolean value);
 
public void setState(boolean value);
  
public boolean toggle();
+
...
 
+
public void pulse(long duration, boolean pulseState);
+
 
+
public void blink(long delay, long duration, boolean blinkState);
+
  
 
}
 
}
Line 106: Line 112:
 
Since the implementation of this interface already exists, all we need to do use the IGPIOPinOutput service is to write our application code so that OSGi service instances of this type are accessed for the pins we are interested in manipulating.   
 
Since the implementation of this interface already exists, all we need to do use the IGPIOPinOutput service is to write our application code so that OSGi service instances of this type are accessed for the pins we are interested in manipulating.   
  
To find and use an instance of IGPIOPinOutput we can use one of several OSGi mechanisms:  1) Declarative Services; 2) ServiceTracker; 3) BundleContext.  For this tutorial, we will use a very simple ServiceTracker.  The full code for this example can be located in the start method of [https://github.com/ECF/RaspberryPI/blob/master/test/bundles/org.eclipse.ecf.raspberrypi.test.gpio/src/org/eclipse/ecf/raspberrypi/test/gpio/Activator.java this test code].  The important fragments from this example are the implementation of the '''addingService''' method, which is executed when instances of IGPIOPinOutput are registered in the OSGi service registry
+
To find and use an instance of IGPIOPinOutput we can use one of several OSGi mechanisms:  1) Declarative Services; 2) ServiceTracker; 3) BundleContext.  For this tutorial, we will use a very simple ServiceTracker.  The full code for this example can be located in the registerTracker method of [https://github.com/ECF/RaspberryPI/blob/master/bundles/org.eclipse.ecf.raspberrypi.gpio.pi4j/src/org/eclipse/ecf/raspberrypi/gpio/pi4j/AbstractPinManager.java the AbstractPinManager class].  The important fragments from this example are the implementation of the '''addingService''' method, which is executed when instances of IGPIOPinOutput are registered in the OSGi service registry
  
 
<source lang="java">
 
<source lang="java">
 
public IGPIOPinOutput addingService(ServiceReference<IGPIOPinOutput> reference) {
 
public IGPIOPinOutput addingService(ServiceReference<IGPIOPinOutput> reference) {
    System.out.println("Adding GPIO Pin Output service.   id="+reference.getProperty(IGPIOPinOutput.PIN_ID_PROP));
+
IGPIOPinOutput pin = fBundleContext.getService(reference);
    IGPIOPinOutput pin = context.getService(reference);
+
        ...
    System.out.println("  current pin state is "+(pin.getState()?"HIGH":"LOW"));
+
System.out.println("Adding GPIO Pin Output service. id="+ reference.getProperty(IGPIOPinOutput.PIN_ID_PROP));
    System.out.println("  setting state to HIGH");
+
        System.out.println("  current pin state is "+ (pin.getState() ? "HIGH" : "LOW"));
    pin.setState(true);
+
System.out.println("  setting state to HIGH");
    return pin;
+
pin.setState(true);
 +
return pin;
 
}
 
}
 
</source>
 
</source>
  
Note that when the IGPIOPinService is added, the service.setState(true) method is called, which will set the state of the pin to HIGH, turning on the LED connected to that pin.   
+
When the IGPIOPinService is added, the service.setState(true) method is called, which will set the state of the pin to HIGH, turning on the LED connected to that pin.   
  
 
As well, we will implement '''removedService''' which is executed when instances of IGPIOPinOutput are unregistered.
 
As well, we will implement '''removedService''' which is executed when instances of IGPIOPinOutput are unregistered.
Line 141: Line 148:
 
   current pin state is LOW
 
   current pin state is LOW
 
   setting state to HIGH
 
   setting state to HIGH
TestGPIOPinInputListener.handleInputEvent(event=GPIOPinInputEvent [getPinId()=2, state=false])
 
 
</pre>
 
</pre>
  
And the LED lights up
+
the LED lights up
  
 
[[File:Ledhigh.png]]
 
[[File:Ledhigh.png]]
Line 158: Line 164:
 
and the LED goes out.
 
and the LED goes out.
  
Here is a [https://www.youtube.com/watch?v=BtS_JDyiKkU&feature=youtu.be short video showing the running of this test] code by using the Apache Webconsole.
+
Here is a [https://www.youtube.com/watch?v=BtS_JDyiKkU&feature=youtu.be short video showing the running of this test] code by using the Apache Webconsole.  Thanks to ECF committer Wim Jongman for creating the video.
  
 +
==GPIO Input==
  
 +
In addition to controlling devices that are connected to one or more GPIO pins, it's also possible to receive asynchronous notifications of inputs to digital pins.  To do this with OSGi services we will use a very useful OSGi service pattern called the '''whiteboard pattern'''.  The whiteboard pattern is described in links given under Related Articles below.
 +
 +
For the whiteboard pattern we need another service interface, called IGPIOPinInputListener
 +
 +
<source lang="java">
 +
public interface IGPIOPinInputListener {
 +
 +
void handleInputEvent(GPIOPinInputEvent event);
 +
 +
}
 +
</source>
 +
 +
and the associated GPIOPinInputEvent
 +
 +
<source lang="java">
 +
public class GPIOPinInputEvent extends AbstractGPIOPinEvent implements
 +
Serializable {
 +
 +
private static final long serialVersionUID = -6103636181685626657L;
 +
 +
private final boolean state;
 +
 +
public GPIOPinInputEvent(int pinId, boolean state) {
 +
super(pinId);
 +
this.state = state;
 +
}
 +
 +
public boolean getState() {
 +
return state;
 +
}
 +
 +
@Override
 +
public String toString() {
 +
return "GPIOPinInputEvent [getPinId()=" + getPinId() + ", state="
 +
+ state + "]";
 +
}
 +
 +
}
 +
</source>
 +
 +
With the whiteboard pattern, instead of clients looking up/retrieving services (as with IGPIOPinOutput and the ServiceTracker given above), what's necessary is that the IGPIOPinInputListener be implemented and then an instance registered in the OSGi Service registry.  For example, here's a very simple class that implements IGPIOPinInputListener
 +
 +
<source lang="java">
 +
class TestGPIOPinInputListener implements IGPIOPinInputListener {
 +
    @Override
 +
    public void handleInputEvent(GPIOPinInputEvent event) {
 +
        System.out.println("TestGPIOPinInputListener.handleInputEvent(event="+event+")");
 +
    }
 +
}
 +
</source>
 +
 +
and then we create an instance of this class, create some service properties and register this instance via the OSGi BundleContext (context):
 +
 +
<source lang="java">
 +
// Create properties
 +
Hashtable props = IGPIOPin.Util.createInputListenerProps(IGPIOPin.DEFAULT_INPUT_PIN);
 +
// Create and register listener using context and props created above
 +
context.registerService(IGPIOPinInputListener.class, new TestGPIOPinInputListener(), props);
 +
</source>
 +
 +
This listener is then notified and produces output when the DEFAULT_INPUT_PIN changes it's state:
 +
 +
<pre>
 +
TestGPIOPinInputListener.handleInputEvent(event=GPIOPinInputEvent [getPinId()=2, state=false])
 +
</pre>
 +
 +
All of the code above is provided in the ECF [https://github.com/ECF/RaspberryPI RaspberryPi github repository].  The service interfaces above (IGPIOPinOutput and IGPIOPinInputListener and event classes) are available in the bundles/org.eclipse.ecf.raspberrypi.gpio project.
 +
 +
Also available is a complete working implementation of this service API based upon/using [http://pi4j.com Pi4j].  This implementation is in the bundles/org.eclipse.ecf.raspberrypi.gpio.pi4j project.
 +
 +
==Remote Control for GPIO Pins==
 +
 +
See the [[Tutorial:_OSGi_Remote_Services_for_Raspberry_Pi_GPIO | OSGi Remote Services for Raspberry Pi GPIO tutorial]].
  
 
==Related Articles==
 
==Related Articles==
  
[[Getting Started with ECF's OSGi Remote Services Implementation]]
+
[http://www.osgi.org/wiki/uploads/Links/whiteboard.pdf Listeners Considered Harmful: The 'Whiteboard Pattern']
  
[[Asynchronous Proxies for Remote Services]]
+
[http://www.knopflerfish.org/osgi_service_tutorial.html#white The whiteboard model]
  
[[Tutorial:_Building_your_first_Asynchronous_OSGi_Remote_Service | Building your first Asynchronous OSGi Remote Service Tutorial]]
+
[[Tutorial:_OSGi_Remote_Services_for_Raspberry_Pi_GPIO | OSGi Remote Services for Raspberry Pi GPIO]]
 +
 
 +
[[Getting Started with ECF's OSGi Remote Services Implementation]]
  
 
[[EIG:Download|Download ECF Remote Services/RSA Implementation]]
 
[[EIG:Download|Download ECF Remote Services/RSA Implementation]]
  
 
[[EIG:Add to Target Platform|How to Add Remote Services/RSA to Your Target Platform]]
 
[[EIG:Add to Target Platform|How to Add Remote Services/RSA to Your Target Platform]]

Latest revision as of 03:09, 28 April 2016


Introduction

This tutorial shows how to use OSGi Services to access the Raspberry Pi GPIO. The use of OSGi Services to use GPIO provides several important advantages:

  1. The OSGi Service Interface provides a simple and clear yet flexible software abstraction
    1. Clear Contract Between Service Producer and Service Consumer
    2. Loose Coupling and High Cohesion
    3. Flexible yet Simple Abstractions
  2. OSGi Services provides technical advantages
    1. Service Dynamics - Services come and go, applications can be notified and adjust
    2. Service Versioning - Services evolve over time
    3. Variety of Injection Frameworks - e.g. Declarative Services, Blueprint/Spring
    4. Network Services - ECF Remote Services
    5. Service-Level Security

GPIO Pin Output

Each GPIO digital pins have two modes: output and input. For output, each pin can be in either the 'HIGH' state, or the 'LOW' state, and the control of the pin state is what we would like to manipulate to give our application behavior. After connecting a single LED, changing the output on a given pin to HIGH will turn the LED on, while setting to low will turn it off. See this page for a description of how to setup such an LED.

We would like to use OSGi services to simplify software access to devices connected to the GPIO digital pins. Here's a picture of a Raspberry Pi setup with an LED connected to GPIO digital output pin 0, as per the instructions here. This is the setup that I use to test the services described below

Ledsetup.png

For OSGi services, we start by creating a service interface declaring the methods for accessing a single GPIO pin. Here is the IGPIOPinOutput service interface:

public interface IGPIOPinOutput extends IGPIOPin {
 
	public boolean getState();
 
	public void setState(boolean value);
 
...
 
}

For this tutorial, we will be interested only in the getState and setState methods from this service interfaces. Note that we can create as many instances of this service interface as desired, but are limited by the GPIO device itself, which has two rows of 10 pins for a total of 20 physical pins.

Since the implementation of this interface already exists, all we need to do use the IGPIOPinOutput service is to write our application code so that OSGi service instances of this type are accessed for the pins we are interested in manipulating.

To find and use an instance of IGPIOPinOutput we can use one of several OSGi mechanisms: 1) Declarative Services; 2) ServiceTracker; 3) BundleContext. For this tutorial, we will use a very simple ServiceTracker. The full code for this example can be located in the registerTracker method of the AbstractPinManager class. The important fragments from this example are the implementation of the addingService method, which is executed when instances of IGPIOPinOutput are registered in the OSGi service registry

public IGPIOPinOutput addingService(ServiceReference<IGPIOPinOutput> reference) {
	IGPIOPinOutput pin = fBundleContext.getService(reference);
        ...
	System.out.println("Adding GPIO Pin Output service. id="+ reference.getProperty(IGPIOPinOutput.PIN_ID_PROP));
        System.out.println("  current pin state is "+ (pin.getState() ? "HIGH" : "LOW"));
	System.out.println("  setting state to HIGH");
	pin.setState(true);
	return pin;
}

When the IGPIOPinService is added, the service.setState(true) method is called, which will set the state of the pin to HIGH, turning on the LED connected to that pin.

As well, we will implement removedService which is executed when instances of IGPIOPinOutput are unregistered.

public void removedService(ServiceReference<IGPIOPinOutput> reference,
                           IGPIOPinOutput service) {
    System.out.println("Removing GPIO Pin service. id="+reference.getProperty(IGPIOPinOutput.PIN_ID_PROP));
    System.out.println("  setting state to LOW");
    service.setState(false);
}

When the service is removed, the service.setState(false) is called, which will set the state of pin 0 to LOW, turning off the LED connected to that pin.

When this test bundle is run on a Raspberry Pi (using Java8+Equinox 4.4), this appears on the OSGi console:

osgi> start 3
Adding GPIO Pin Output service.   id=0
  current pin state is LOW
  setting state to HIGH

the LED lights up

Ledhigh.png

If we then stop this test bundle, this appears on the OSGi console

osgi> stop 3
Removing GPIO Pin service. id=0
  setting state to LOW

and the LED goes out.

Here is a short video showing the running of this test code by using the Apache Webconsole. Thanks to ECF committer Wim Jongman for creating the video.

GPIO Input

In addition to controlling devices that are connected to one or more GPIO pins, it's also possible to receive asynchronous notifications of inputs to digital pins. To do this with OSGi services we will use a very useful OSGi service pattern called the whiteboard pattern. The whiteboard pattern is described in links given under Related Articles below.

For the whiteboard pattern we need another service interface, called IGPIOPinInputListener

public interface IGPIOPinInputListener {
 
	void handleInputEvent(GPIOPinInputEvent event);
 
}

and the associated GPIOPinInputEvent

public class GPIOPinInputEvent extends AbstractGPIOPinEvent implements
		Serializable {
 
	private static final long serialVersionUID = -6103636181685626657L;
 
	private final boolean state;
 
	public GPIOPinInputEvent(int pinId, boolean state) {
		super(pinId);
		this.state = state;
	}
 
	public boolean getState() {
		return state;
	}
 
	@Override
	public String toString() {
		return "GPIOPinInputEvent [getPinId()=" + getPinId() + ", state="
				+ state + "]";
	}
 
}

With the whiteboard pattern, instead of clients looking up/retrieving services (as with IGPIOPinOutput and the ServiceTracker given above), what's necessary is that the IGPIOPinInputListener be implemented and then an instance registered in the OSGi Service registry. For example, here's a very simple class that implements IGPIOPinInputListener

class TestGPIOPinInputListener implements IGPIOPinInputListener {
    @Override
    public void handleInputEvent(GPIOPinInputEvent event) {
        System.out.println("TestGPIOPinInputListener.handleInputEvent(event="+event+")");
    }
}

and then we create an instance of this class, create some service properties and register this instance via the OSGi BundleContext (context):

// Create properties
Hashtable props = IGPIOPin.Util.createInputListenerProps(IGPIOPin.DEFAULT_INPUT_PIN);
// Create and register listener using context and props created above
context.registerService(IGPIOPinInputListener.class, new TestGPIOPinInputListener(), props);

This listener is then notified and produces output when the DEFAULT_INPUT_PIN changes it's state:

TestGPIOPinInputListener.handleInputEvent(event=GPIOPinInputEvent [getPinId()=2, state=false])

All of the code above is provided in the ECF RaspberryPi github repository. The service interfaces above (IGPIOPinOutput and IGPIOPinInputListener and event classes) are available in the bundles/org.eclipse.ecf.raspberrypi.gpio project.

Also available is a complete working implementation of this service API based upon/using Pi4j. This implementation is in the bundles/org.eclipse.ecf.raspberrypi.gpio.pi4j project.

Remote Control for GPIO Pins

See the OSGi Remote Services for Raspberry Pi GPIO tutorial.

Related Articles

Listeners Considered Harmful: The 'Whiteboard Pattern'

The whiteboard model

OSGi Remote Services for Raspberry Pi GPIO

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

Back to the top