From 1516a1a15554370c5faecd798c92163ddc6ea172 Mon Sep 17 00:00:00 2001 From: Jeroen Kransen Date: Sun, 5 Jan 2014 16:21:21 +0100 Subject: [PATCH] Added apply for constructor of actors --- README.md | 53 +++++++++++++------ pom.xml | 2 +- .../scala/framboos/actor/InPinActor.scala | 4 ++ .../scala/framboos/actor/OutPinActor.scala | 4 ++ .../framboos/actor/SerialPortActor.scala | 26 ++++----- src/main/scala/framboos/actor/WireUp.scala | 14 +++-- .../framboos/async/ObservableInPin.scala | 3 +- 7 files changed, 68 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index cda4801..e34111c 100644 --- a/README.md +++ b/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: @@ -36,7 +42,8 @@ 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: @@ -44,9 +51,11 @@ Sample scala code to observe an input pin: 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) @@ -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: @@ -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 ----- @@ -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 @@ -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. diff --git a/pom.xml b/pom.xml index c7d7d2d..8638282 100644 --- a/pom.xml +++ b/pom.xml @@ -139,5 +139,5 @@ sparetimelabs http://www.sparetimelabs.com/maven2 - + diff --git a/src/main/scala/framboos/actor/InPinActor.scala b/src/main/scala/framboos/actor/InPinActor.scala index 492cf4e..dd3021e 100644 --- a/src/main/scala/framboos/actor/InPinActor.scala +++ b/src/main/scala/framboos/actor/InPinActor.scala @@ -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._ diff --git a/src/main/scala/framboos/actor/OutPinActor.scala b/src/main/scala/framboos/actor/OutPinActor.scala index 647aa8c..6ceb57b 100644 --- a/src/main/scala/framboos/actor/OutPinActor.scala +++ b/src/main/scala/framboos/actor/OutPinActor.scala @@ -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) diff --git a/src/main/scala/framboos/actor/SerialPortActor.scala b/src/main/scala/framboos/actor/SerialPortActor.scala index d431531..53cfdfc 100644 --- a/src/main/scala/framboos/actor/SerialPortActor.scala +++ b/src/main/scala/framboos/actor/SerialPortActor.scala @@ -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 @@ -18,7 +19,7 @@ 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._ @@ -26,19 +27,21 @@ class SerialPortActor(portName: String) extends Actor { 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) } } } @@ -46,12 +49,12 @@ class SerialPortActor(portName: String) extends Actor { }) 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") } } } @@ -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") } } @@ -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 { - } } diff --git a/src/main/scala/framboos/actor/WireUp.scala b/src/main/scala/framboos/actor/WireUp.scala index 8493dc9..555cfe1 100644 --- a/src/main/scala/framboos/actor/WireUp.scala +++ b/src/main/scala/framboos/actor/WireUp.scala @@ -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") } } } diff --git a/src/main/scala/framboos/async/ObservableInPin.scala b/src/main/scala/framboos/async/ObservableInPin.scala index 216daec..41a2383 100644 --- a/src/main/scala/framboos/async/ObservableInPin.scala +++ b/src/main/scala/framboos/async/ObservableInPin.scala @@ -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