Skip to content

Commit

Permalink
Added apply for constructor of actors
Browse files Browse the repository at this point in the history
  • Loading branch information
jkransen committed Jan 5, 2014
1 parent 25adabb commit 1516a1a
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 38 deletions.
53 changes: 38 additions & 15 deletions README.md
@@ -1,7 +1,13 @@
Java GPIO access
----------------

Framboos is a small Java wrapper around the default GPIO driver on Linux boards like Raspberry Pi and BeagleBoard. For this reason it does not depend on any additional native libraries. The program that uses it must be run as root in order to access the driver files under /sys/class/gpio/. It supports input and output pins, as well as serial (UART) communication. It does not support PWM, SPI or I2C. If you need any of that, use [Pi4J](http://pi4j.com) instead, which includes native code and the wiringPi library. This is meant to be a pure Java (Scala) implementation that can be added as a simple Maven dependency to any Java project.
Framboos is a small Java wrapper around the default GPIO driver on Linux boards like Raspberry Pi
and BeagleBoard. It does not depend on any additional native libraries. The program that uses it
must be run as root in order to access the driver files under /sys/class/gpio/. It supports input
and output pins, as well as serial (UART) communication. It does not support PWM, SPI or I2C. If
you need any of that, use [Pi4J](http://pi4j.com) instead, which includes native code and the
wiringPi library. This is meant to be a pure Java (Scala) implementation that can be added as a
simple Maven dependency to any Java project.

Java code example to use an input pin:

Expand Down Expand Up @@ -36,17 +42,20 @@ It is important to close pins that are created, so that they are released for ot
Extensions for Akka and asynchronous programming
------------------------------------------------

Instead of creating and polling an input pin yourself, you can create an Observer that will run a function any time the pin value changes.
Instead of creating and polling an input pin yourself, you can create an Observer that will run
a function any time the pin value changes.

Sample scala code to observe an input pin:

import framboos.async.ObservableInPin
val inPin = ObservableInPin(8)
inPin.subscribe(newValue => println(s"New value $newValue")

In an Akka application, you can create Actors that create GPIO/UART output based on received Akka messages, or that send Akka messages themselves on changed GPIO/UART input.
In an Akka application, you can create Actors that create GPIO/UART output based on received
Akka messages, or that send Akka messages themselves on changed GPIO/UART input.

WireUp is a provided sample Akka actor, that shows what the InPinActor, OutPinActor and SerialPortActor can do:
WireUp is a provided sample Akka actor, that shows what the InPinActor, OutPinActor and
SerialPortActor can do:

val inPin8 = context.actorOf(Props(new InPinActor(8)), name = "inPin8")
inPin8 ! AddListener(self)
Expand All @@ -68,16 +77,25 @@ WireUp is a provided sample Akka actor, that shows what the InPinActor, OutPinAc
}
}

InPinActor will send NewValue messages, containing a boolean of the new value. SerialPortActor will send ReceiveMessage messages for every line of text received on the serial port (which here is /dev/ttyAMA0, the default serial Rx/Tx pins on the GPIO header of the Raspberry Pi).
InPinActor will send NewValue messages, containing a boolean of the new value. SerialPortActor
will send ReceiveMessage messages for every line of text received on the serial port (which here
is /dev/ttyAMA0, the default serial Rx/Tx pins on the GPIO header of the Raspberry Pi).

OutPin will accept NewValue(boolean) messages, and set the output pin accordingly. SerialPortActor acccepts SendMessage messages as well, and will send the containing String over the serial line.
OutPin will accept NewValue(boolean) messages, and set the output pin accordingly. SerialPortActor
acccepts SendMessage messages as well, and will send the containing String over the serial line.

Wired up like this, incoming serial input will be sent back with a prefix, and make the LED light up. Pressing the button will light the LED as well, and send a text over the serial line. Releasing the button will make the LED go off (even if it was triggered by incoming serial text).
Wired up like this, incoming serial input will be sent back with a prefix, and make the LED light
up. Pressing the button will light the LED as well, and send a text over the serial line. Releasing
the button will make the LED go off (even if it was triggered by incoming serial text).

Pin assignment
--------------

When passing an integer to a constructor it will default to the wiringPi layout. When passing a String representation (i.e. "GPIO2_1" for BeagleBone) it will directly address the pin, bypassing the wiringPi numbering. Note that this library does not depend on or use wiringPi under the hood, but by default it uses its numbering scheme instead of e.g. the native Broadcom numbers in the case of Raspberry Pi.
When passing an integer to a constructor it will default to the wiringPi layout. When passing a
String representation (i.e. "GPIO2_1" for BeagleBone) it will directly address the pin, bypassing
the wiringPi numbering. Note that this library does not depend on or use wiringPi under the hood,
but by default it uses its numbering scheme instead of e.g. the native Broadcom numbers in the
case of Raspberry Pi.

GPIO pins in the numbering used by wiringPi:

Expand All @@ -99,13 +117,13 @@ Provided sample code

Next to the wrapper classes, I made some classes that make connected LEDs light up in different
patterns. For this to work, you need to connect the LEDs like I did. I use the Starter Kit from
SK Pang, which contains a couple of LEDs, 2 buttons and 10 wires. At first I used 9 LEDs minus the
number of buttons used, as the Starter Kit has limited wires, but later I decided to wire all 9 LEDs
with custom wires, so there is no need to rewire when switching algorithms.
SK Pang, which contains a couple of LEDs, 2 buttons and 10 wires. At first I used 9 LEDs minus
the number of buttons used, as the Starter Kit has limited wires, but later I decided to wire all
9 LEDs with custom wires, so there is no need to rewire when switching algorithms.

For the Nine LED algorithms, connect pins 0 to 7 in that order to the LEDs, and 10 as ninth.
Add button 1 to pin 8, and button 2 to pin 9. These pins have pull-up resistors to allow simple buttons that
short-circuit when pressed.
Add button 1 to pin 8, and button 2 to pin 9. These pins have pull-up resistors to allow simple
buttons that short-circuit when pressed.

Usage
-----
Expand All @@ -128,7 +146,8 @@ To run one of the patterns, run the application itself inside the jar:

java -jar target/framboos-X.Y.Z.jar

This will pick a random pattern to light up the LEDs. You can also specify the pattern you want to run, like this:
This will pick a random pattern to light up the LEDs. You can also specify the pattern you want
to run, like this:

java -jar target/framboos-X.Y.Z.jar caterpillar

Expand All @@ -139,5 +158,9 @@ For a complete list of available pattern algorithms, examine this file:
Wiring notes
------------

Please do use the resistors that come with the Starter Kit (or your own resistors if you don't use that kit) when connecting LEDs, or the current will fry them. Connect them serially, which means like this: connect one leg of the resistor to the - (minus) bottom line of the breadboard, where you also connected the ground of the Raspberry Pi. Connect the other side of the resistor with the short leg of the LED. Connect the long leg of the LED to whatever port you
Please do use the resistors that come with the Starter Kit (or your own resistors if you don't
use that kit) when connecting LEDs, or the current will fry them. Connect them serially, which
means like this: connect one leg of the resistor to the - (minus) bottom line of the breadboard,
where you also connected the ground of the Raspberry Pi. Connect the other side of the resistor
with the short leg of the LED. Connect the long leg of the LED to whatever port you
connect it to.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -139,5 +139,5 @@
<id>sparetimelabs</id>
<url>http://www.sparetimelabs.com/maven2</url>
</repository>
</repositories>
</repositories>
</project>
4 changes: 4 additions & 0 deletions src/main/scala/framboos/actor/InPinActor.scala
Expand Up @@ -4,6 +4,10 @@ import akka.actor._
import framboos._
import framboos.async._

object InPinActor {
def props(pinNumber: Int): Props = Props(new InPinActor(pinNumber))
}

class InPinActor(pinNumber: Int) extends Actor {

import CommonMessages._
Expand Down
4 changes: 4 additions & 0 deletions src/main/scala/framboos/actor/OutPinActor.scala
Expand Up @@ -4,6 +4,10 @@ import akka.actor._
import framboos._
import CommonMessages._

object OutPinActor {
def props(pinNumber: Int): Props = Props(new OutPinActor(pinNumber))
}

class OutPinActor(pinNumber: Int) extends Actor {

val outPin = OutPin(pinNumber)
Expand Down
26 changes: 13 additions & 13 deletions src/main/scala/framboos/actor/SerialPortActor.scala
Expand Up @@ -10,6 +10,7 @@ import java.io._
import CommonMessages._

object SerialPortActor {
def props(portName: String): Props = Props(new SerialPortActor(portName))

/** Message to be sent over serial connection */
case class SendMessage(message: String) extends Incoming
Expand All @@ -18,40 +19,42 @@ object SerialPortActor {
case class ReceiveMessage(message: String) extends Outgoing
}

class SerialPortActor(portName: String) extends Actor {
class SerialPortActor(portName: String) extends Actor with ActorLogging {

import SerialPortActor._

var listeners = Set.empty[ActorRef]

def receive = connecting

async {
connect

def connect = async {
findPort(portName) match {
case Some(port) => {
println(s"Port found: $portName")
log.info(s"Port found: $portName")
val in = new BufferedReader(new InputStreamReader(port.getInputStream))

port.addEventListener(new SerialPortEventListener {
def serialEvent(event: SerialPortEvent) {
if (event.getEventType == SerialPortEvent.DATA_AVAILABLE) {
println(s"New serial input")
log.debug("New serial input")
while (in.ready) {
val nextLine = in.readLine
println(nextLine)
log.debug(s"Receiving message: $nextLine")
listeners foreach { _ ! ReceiveMessage(nextLine) }
}
}
}
})

val out = new BufferedWriter(new OutputStreamWriter(port.getOutputStream))
out.write("Hello from SerialPortActor, please stand by for messages\n")
out.write("Hello from SerialPortActor\n")
out.flush
context.become(connected(in, out), true)
}
case None => {
println(s"Could not find port $portName")
log.error(s"Could not find port $portName")
}
}
}
Expand Down Expand Up @@ -90,7 +93,7 @@ class SerialPortActor(portName: String) extends Actor {
listeners = listeners - listener
}
case SendMessage(message: String) => async {
println(s"Not connected, could not deliver message:\n$message")
log.warning(s"Not connected, could not deliver message: $message")
}
}

Expand All @@ -102,12 +105,9 @@ class SerialPortActor(portName: String) extends Actor {
listeners = listeners - listener
}
case SendMessage(message: String) => async {
println(s"Sending message: $message")
out.write(message)
log.debug(s"Sending message: $message")
out.write(message + '\n')
out.flush
}
}

override def postStop {
}
}
14 changes: 6 additions & 8 deletions src/main/scala/framboos/actor/WireUp.scala
Expand Up @@ -6,25 +6,23 @@ import CommonMessages._

class WireUp extends Actor {

val inPin8 = context.actorOf(Props(new InPinActor(8)), name = "inPin8")

val inPin8 = context.actorOf(InPinActor.props(pinNumber = 8), name = "inPin8")
inPin8 ! AddListener(self)

val outPin0 = context.actorOf(Props(new OutPinActor(0)), name = "outPin0")

val serialPort = context.actorOf(Props(new SerialPortActor("ttyAMA0")), name = "serialPort")
val outPin0 = context.actorOf(OutPinActor.props(pinNumber = 0), name = "outPin0")

val serialPort = context.actorOf(SerialPortActor.props(portName = "ttyAMA0"), name = "serialPort")
serialPort ! AddListener(self)

def receive: Receive = {
case NewValue(value: Boolean) => {
outPin0 ! NewValue(value)
val pressed = if (value) "pressed" else "released"
serialPort ! SendMessage(s"Button $pressed at ${System.currentTimeMillis}\n")
serialPort ! SendMessage(s"Button $pressed on ${System.currentTimeMillis}")
}
case SerialPortActor.ReceiveMessage(message: String) => {
case ReceiveMessage(message: String) => {
outPin0 ! NewValue(true)
serialPort ! SendMessage(s"Received your message: $message\n")
serialPort ! SendMessage(s"Received your message: $message")
}
}
}
3 changes: 2 additions & 1 deletion src/main/scala/framboos/async/ObservableInPin.scala
Expand Up @@ -16,7 +16,8 @@ object ObservableInPin {
intervals.subscribe(next => {
val currentValue = inPin.value
if (currentValue != lastValue) {
println(s"value of in#$pinNumber changed to $currentValue")
// TODO access Akka logging?
// log.debug(s"value of in#$pinNumber changed to $currentValue")
subject.onNext(currentValue)
}
lastValue = currentValue
Expand Down

0 comments on commit 1516a1a

Please sign in to comment.