├── .gitignore
├── src
├── main
│ ├── java
│ │ └── framboos
│ │ │ ├── algorithm
│ │ │ ├── Algorithm.java
│ │ │ ├── util
│ │ │ │ └── Timer.java
│ │ │ ├── RandomAll.java
│ │ │ ├── AllOn.java
│ │ │ ├── Blink.java
│ │ │ ├── Caterpillar.java
│ │ │ ├── GoRight.java
│ │ │ ├── RandomOne.java
│ │ │ ├── NineLedsAlgorithm.java
│ │ │ ├── BinaryCount.java
│ │ │ ├── ChangeOnButton.java
│ │ │ ├── ZigZag.java
│ │ │ ├── CaterpillarZigZag.java
│ │ │ ├── ButtonChangeDirection.java
│ │ │ ├── EightLedsOneButtonAlgorithm.java
│ │ │ ├── Tennis.java
│ │ │ ├── SevenLedsTwoButtonsAlgorithm.java
│ │ │ └── NineLedsTwoButtonsAlgorithm.java
│ │ │ ├── util
│ │ │ └── HardReset.java
│ │ │ └── App.java
│ ├── scala
│ │ └── framboos
│ │ │ ├── InPin.scala
│ │ │ ├── actor
│ │ │ ├── CommonMessages.scala
│ │ │ ├── OutPinActor.scala
│ │ │ ├── InPinActor.scala
│ │ │ ├── WireUp.scala
│ │ │ └── SerialPortActor.scala
│ │ │ ├── ReverseInPin.scala
│ │ │ ├── OutPin.scala
│ │ │ ├── async
│ │ │ └── ObservableInPin.scala
│ │ │ └── GpioPin.scala
│ └── resources
│ │ └── META-INF
│ │ └── services
│ │ └── framboos.algorithm.Algorithm
└── site
│ └── apt
│ └── pins.apt
├── pom.xml
├── README.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | .settings/
3 | .project
4 | .classpath
5 | .cache
6 | *.class
7 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/Algorithm.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | public interface Algorithm {
4 |
5 | public void setUp();
6 |
7 | public void execute();
8 |
9 | public void tearDown();
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/util/Timer.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm.util;
2 |
3 | public class Timer {
4 |
5 | public static void pause() {
6 | pause(100); // default .1 sec
7 | }
8 |
9 | public static void pause(int ms) {
10 | try {
11 | Thread.sleep(Math.max(0, ms));
12 | } catch (InterruptedException e) {
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/InPin.scala:
--------------------------------------------------------------------------------
1 | package framboos
2 |
3 | import GpioPin._
4 |
5 | class InPin(pinNumber: Int, isDirect: Boolean) extends GpioPin(pinNumber, In, isDirect) {
6 | def this(pinNumber: Int) = this(mappedPins(pinNumber), isDirect = false)
7 | def this(pinName: String) = this(getPinNumber(pinName), isDirect = true)
8 | }
9 |
10 | object InPin {
11 | def apply(pinNumber: Int) = new InPin(pinNumber)
12 | }
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/RandomAll.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class RandomAll extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | while (true) {
10 | for (OutPin pin : pins) {
11 | pin.setValue(Math.random() < 0.5);
12 | }
13 | Timer.pause();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/actor/CommonMessages.scala:
--------------------------------------------------------------------------------
1 | package framboos.actor
2 |
3 | import akka.actor._
4 |
5 | object CommonMessages {
6 |
7 | trait Incoming
8 |
9 | /** listener actor to be added */
10 | case class AddListener(listener: ActorRef)
11 | /** listener actor to be removed */
12 | case class RemoveListener(listener: ActorRef)
13 |
14 | trait Outgoing
15 |
16 | case class NewValue(value: Boolean)
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/AllOn.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class AllOn extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | for (OutPin pin : pins) {
10 | pin.setValue(true);
11 | }
12 |
13 | while (true) {
14 | // no changes except on button pressed
15 | Timer.pause();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/Blink.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class Blink extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | boolean isOne = false;
10 |
11 | while (true) {
12 | for (OutPin pin : pins) {
13 | pin.setValue(isOne);
14 | }
15 | isOne = !isOne;
16 | Timer.pause();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/Caterpillar.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class Caterpillar extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | boolean wasOne = false;
10 |
11 | while (true) {
12 | for (OutPin pin : pins) {
13 | pin.setValue(!wasOne);
14 | Timer.pause();
15 | }
16 | wasOne = !wasOne;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/ReverseInPin.scala:
--------------------------------------------------------------------------------
1 | package framboos;
2 | import GpioPin._
3 |
4 | class ReverseInPin(pinNumber: Int, isDirect: Boolean) extends InPin(pinNumber, isDirect) {
5 |
6 | def this(pinNumber: Int) = this(mappedPins(pinNumber), isDirect = false)
7 | def this(pinName: String) = this(getPinNumber(pinName), isDirect = true)
8 |
9 | override def value = !super.value
10 | }
11 |
12 | object ReverseInPin {
13 | def apply(pinNumber: Int) = new ReverseInPin(pinNumber)
14 | }
--------------------------------------------------------------------------------
/src/main/scala/framboos/actor/OutPinActor.scala:
--------------------------------------------------------------------------------
1 | package framboos.actor
2 |
3 | import akka.actor._
4 | import framboos._
5 | import CommonMessages._
6 |
7 | object OutPinActor {
8 | def props(pinNumber: Int): Props = Props(new OutPinActor(pinNumber))
9 | }
10 |
11 | class OutPinActor(pinNumber: Int) extends Actor {
12 |
13 | val outPin = OutPin(pinNumber)
14 |
15 | def receive: Receive = {
16 | case NewValue(value) => {
17 | outPin.setValue(value)
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/GoRight.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class GoRight extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | int i = 0;
10 |
11 | while (true) {
12 | for (int j = 0; j < pins.length; j++) {
13 | OutPin pin = pins[j];
14 | pin.setValue(j % 3 == i);
15 | }
16 | i = (i + 1) % 3;
17 | Timer.pause();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/RandomOne.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class RandomOne extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | int previous = 0;
10 | while (true) {
11 | int current = (int)(Math.random() * pins.length);
12 | pins[previous].setValue(false);
13 | pins[current].setValue(true);
14 | previous = current;
15 | Timer.pause();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/framboos.algorithm.Algorithm:
--------------------------------------------------------------------------------
1 | # Nine leds, no buttons
2 | framboos.algorithm.AllOn
3 | framboos.algorithm.BinaryCount
4 | framboos.algorithm.Blink
5 | framboos.algorithm.Caterpillar
6 | framboos.algorithm.CaterpillarZigZag
7 | framboos.algorithm.GoRight
8 | framboos.algorithm.RandomAll
9 | framboos.algorithm.RandomOne
10 | framboos.algorithm.ZigZag
11 |
12 | # Nine leds, one button
13 | framboos.algorithm.ChangeOnButton
14 | framboos.algorithm.ButtonChangeDirection
15 |
16 | # Nine leds, two buttons
17 | framboos.algorithm.Tennis
18 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/NineLedsAlgorithm.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 |
5 | public abstract class NineLedsAlgorithm implements Algorithm {
6 |
7 | private final OutPin[] pins = new OutPin[9];
8 |
9 | public void setUp() {
10 | for (int i = 0; i <= 8; i++) {
11 | pins[i] = new OutPin(i);
12 | }
13 | }
14 |
15 | public void tearDown() {
16 | for (OutPin pin : pins) {
17 | pin.close();
18 | }
19 | }
20 |
21 | public void execute() {
22 | lightLeds(pins);
23 | }
24 |
25 | public abstract void lightLeds(OutPin[] pins);
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/BinaryCount.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class BinaryCount extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | int binaryNumber = 0;
10 |
11 | while (true) {
12 | for (int j = 0; j < pins.length; j++) {
13 | if ((binaryNumber & (1 << j)) > 0) {
14 | pins[j].setValue(true);
15 | }
16 | else {
17 | pins[j].setValue(false);
18 | }
19 | }
20 | binaryNumber++;
21 |
22 | Timer.pause();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/ChangeOnButton.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class ChangeOnButton extends NineLedsTwoButtonsAlgorithm {
7 |
8 | @Override
9 | public void lightLeds(OutPin[] pins) {
10 | for (int i = 0; i < pins.length; i++) {
11 | pins[i].setValue(i % 2 == 0);
12 | }
13 | while (true) {
14 | // no changes except on button pressed
15 | Timer.pause();
16 | }
17 | }
18 |
19 | @Override
20 | public void handleButton1Pressed() {
21 | for (OutPin pin : getLeds()) {
22 | pin.setValue(!pin.getValue());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/ZigZag.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class ZigZag extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | boolean goUp = true;
10 |
11 | int previous = 0;
12 | int i = 0;
13 |
14 | while (true) {
15 | pins[previous].setValue(false);
16 | pins[i].setValue(true);
17 |
18 | if (i == pins.length - 1) {
19 | goUp = false;
20 | }
21 |
22 | if (i == 0) {
23 | goUp = true;
24 | }
25 |
26 | previous = i;
27 | i = goUp ? i + 1 : i - 1;
28 |
29 | Timer.pause();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/OutPin.scala:
--------------------------------------------------------------------------------
1 | package framboos;
2 |
3 | import GpioPin._
4 |
5 | class OutPin(pinNumber: Int, isDirect: Boolean) extends GpioPin(pinNumber, Out, isDirect) {
6 | def this(pinNumber: Int) = this(mappedPins(pinNumber), isDirect = false)
7 | def this(pinName: String) = this(getPinNumber(pinName), isDirect = true)
8 |
9 | // initially set pin to off
10 | setValue(false)
11 |
12 | def setValue(isOne: Boolean) {
13 | if (!isClosing) {
14 | writeFile(valuePath(pinNumber), if (isOne) "1" else "0")
15 | }
16 | }
17 |
18 | override def close {
19 | setValue(false)
20 | super.close
21 | }
22 | }
23 |
24 | object OutPin {
25 | def apply(pinNumber: Int) = new OutPin(pinNumber)
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/framboos/util/HardReset.java:
--------------------------------------------------------------------------------
1 | package framboos.util;
2 |
3 | import java.io.FileOutputStream;
4 | import java.io.IOException;
5 |
6 | import framboos.GpioPin;
7 |
8 | public class HardReset {
9 | static final int [] mappedPins = GpioPin.mappedPins();
10 |
11 | public static boolean hardResetPin(int pinNumber) {
12 | if ((pinNumber < 0) || (pinNumber > 16))
13 | return false;
14 | try {
15 | pinNumber = mappedPins[pinNumber];
16 | String valueString = Integer.toString(pinNumber);
17 | FileOutputStream fos = new FileOutputStream(GpioPin.unexportPath());
18 | fos.write(valueString.getBytes());
19 | fos.close();
20 | } catch (IOException e) {
21 | return false;
22 | }
23 | return true;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/CaterpillarZigZag.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class CaterpillarZigZag extends NineLedsTwoButtonsAlgorithm {
7 |
8 | public void lightLeds(OutPin[] pins) {
9 | boolean goesUp = true;
10 | boolean wasOne = false;
11 |
12 | while (true) {
13 | if (goesUp) {
14 | for (int i = 0; i < pins.length; i++) {
15 | pins[i].setValue(!wasOne);
16 | Timer.pause();
17 | }
18 | wasOne = !wasOne;
19 | }
20 | else {
21 | for (int i = pins.length - 1; i >= 0; i--) {
22 | pins[i].setValue(!wasOne);
23 | Timer.pause();
24 | }
25 | wasOne = !wasOne;
26 | }
27 | if (!wasOne) {
28 | goesUp = !goesUp;
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/ButtonChangeDirection.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class ButtonChangeDirection extends NineLedsTwoButtonsAlgorithm {
7 |
8 | int previous = 0;
9 | boolean goesUp = true;
10 |
11 | @Override
12 | public void lightLeds(OutPin[] leds) {
13 | while (true) {
14 | int current = goesUp ? previous + 1 : previous - 1;
15 | if (current < 0) {
16 | current += leds.length;
17 | }
18 | if (current >= leds.length) {
19 | current -= leds.length;
20 | }
21 | leds[previous].setValue(false);
22 | leds[current].setValue(true);
23 | previous = current;
24 | Timer.pause();
25 | }
26 | }
27 |
28 | @Override
29 | public void handleButton1Pressed() {
30 | goesUp = !goesUp;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/actor/InPinActor.scala:
--------------------------------------------------------------------------------
1 | package framboos.actor
2 |
3 | import akka.actor._
4 | import framboos._
5 | import framboos.async._
6 |
7 | object InPinActor {
8 | def props(pinNumber: Int): Props = Props(new InPinActor(pinNumber))
9 | }
10 |
11 | class InPinActor(pinNumber: Int) extends Actor {
12 |
13 | import CommonMessages._
14 |
15 | val inPin = ObservableInPin(pinNumber)
16 |
17 | var listeners = Set.empty[ActorRef]
18 |
19 | val subscription = inPin.subscribe(newValue => listeners.foreach { _ ! NewValue(newValue) })
20 |
21 | def receive: Receive = {
22 | case AddListener(listener: ActorRef) => {
23 | listeners = listeners + listener
24 | }
25 | case RemoveListener(listener: ActorRef) => {
26 | listeners = listeners - listener
27 | }
28 | }
29 |
30 | override def postStop {
31 | subscription.unsubscribe
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/async/ObservableInPin.scala:
--------------------------------------------------------------------------------
1 | package framboos.async
2 |
3 | import rx.lang.scala.Observable
4 | import rx.lang.scala.Subject
5 | import rx.lang.scala.subjects._
6 | import scala.concurrent.duration._
7 | import framboos._
8 |
9 | object ObservableInPin {
10 |
11 | def apply(pinNumber: Int): Observable[Boolean] = {
12 | val inPin = ReverseInPin(pinNumber)
13 | var lastValue = inPin.value
14 | val subject = BehaviorSubject(lastValue)
15 | val intervals = Observable.interval(50 milliseconds)
16 | intervals.subscribe(next => {
17 | val currentValue = inPin.value
18 | if (currentValue != lastValue) {
19 | // TODO access Akka logging?
20 | // log.debug(s"value of in#$pinNumber changed to $currentValue")
21 | subject.onNext(currentValue)
22 | }
23 | lastValue = currentValue
24 | })
25 | subject
26 | }
27 | }
--------------------------------------------------------------------------------
/src/site/apt/pins.apt:
--------------------------------------------------------------------------------
1 | Wiring
2 |
3 | GPIO pins in the numbering used by wiringPi:
4 |
5 | *---*---*---*---*---*---*---*---*---*---*---*---*---*
6 | | |GND| | | 1 | | 4 | 5 | | 6 | |
7 | *---*---*---*---*---*---*---*---*---*---*---*---*---*
8 | | 8 | | 7 | | 0 | 2 | 3 | | | | |
9 | *---*---*---*---*---*---*---*---*---*---*---*---*---*
10 |
11 | And here as seen when Pi is held upside down:
12 |
13 | *---*---*---*---*---*---*---*---*---*---*---*---*---*
14 | | | | | | 3 | 2 | 0 | | 7 | | 8 |
15 | *---*---*---*---*---*---*---*---*---*---*---*---*---*
16 | | | 6 | | 5 | 4 | | 1 | | |GND| |
17 | *---*---*---*---*---*---*---*---*---*---*---*---*---*
18 |
19 | For simplicity sake, I left out the assignments of the other pins.
20 |
21 | For the Nine LED algorithms, connect pins 0 to 8 in that order to the LEDs.
22 | For the algoithms that use 8 LEDs and a button, replace the LED on pin 8 with
23 | the button.
--------------------------------------------------------------------------------
/src/main/scala/framboos/actor/WireUp.scala:
--------------------------------------------------------------------------------
1 | package framboos.actor
2 |
3 | import akka.actor._
4 | import SerialPortActor._
5 | import CommonMessages._
6 |
7 | class WireUp extends Actor {
8 |
9 | val inPin8 = context.actorOf(InPinActor.props(pinNumber = 8), name = "inPin8")
10 | inPin8 ! AddListener(self)
11 |
12 | val outPin0 = context.actorOf(OutPinActor.props(pinNumber = 0), name = "outPin0")
13 |
14 | val serialPort = context.actorOf(SerialPortActor.props(portName = "ttyAMA0"), name = "serialPort")
15 | serialPort ! AddListener(self)
16 |
17 | def receive: Receive = {
18 | case NewValue(value: Boolean) => {
19 | outPin0 ! NewValue(value)
20 | val pressed = if (value) "pressed" else "released"
21 | serialPort ! SendMessage(s"Button $pressed on ${System.currentTimeMillis}")
22 | }
23 | case ReceiveMessage(message: String) => {
24 | outPin0 ! NewValue(true)
25 | serialPort ! SendMessage(s"Received your message: $message")
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/EightLedsOneButtonAlgorithm.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.ReverseInPin;
5 | import framboos.algorithm.util.Timer;
6 |
7 | public abstract class EightLedsOneButtonAlgorithm implements Algorithm {
8 |
9 | private final OutPin[] pins = new OutPin[8];
10 | private ReverseInPin button;
11 | private boolean isClosed = false;
12 | private boolean buttonWasPressed = false;
13 |
14 | public void setUp() {
15 | for (int i = 0; i <= 7; i++) {
16 | pins[i] = new OutPin(i);
17 | }
18 | button = new ReverseInPin(8);
19 | new Thread() {
20 | public void run() {
21 | while (!isClosed) {
22 | checkButton();
23 | Timer.pause();
24 | }
25 | }
26 | }.start();
27 | }
28 |
29 | public void tearDown() {
30 | for (OutPin pin : pins) {
31 | pin.close();
32 | }
33 | button.close();
34 | isClosed = true;
35 | }
36 |
37 | public void execute() {
38 | lightLeds(pins);
39 | }
40 |
41 | private void checkButton() {
42 | boolean isButtonPressed = button.getValue();
43 | if (isButtonPressed && !buttonWasPressed) {
44 | handleButtonPressed(pins);
45 | }
46 | buttonWasPressed = isButtonPressed;
47 | }
48 |
49 | public abstract void lightLeds(OutPin[] pins);
50 |
51 | public abstract void handleButtonPressed(OutPin[] pins);
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/Tennis.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.OutPin;
4 | import framboos.algorithm.util.Timer;
5 |
6 | public class Tennis extends NineLedsTwoButtonsAlgorithm {
7 |
8 | int previous = 0;
9 | boolean goesUp = true;
10 | boolean isAtButton1 = false;
11 | boolean isAtButton2 = false;
12 | int points1 = 0;
13 | int points2 = 0;
14 | int delay = 200;
15 |
16 | @Override
17 | public void lightLeds(OutPin[] pins) {
18 | while (true) {
19 | int current = goesUp ? previous + 1 : previous - 1;
20 | int numPins = pins.length;
21 |
22 | if (current < 0) {
23 | points2++;
24 | showScore();
25 | current += numPins;
26 | }
27 | if (current >= numPins) {
28 | points1++;
29 | showScore();
30 | current -= numPins;
31 | }
32 | pins[previous].setValue(false);
33 | pins[current].setValue(true);
34 | isAtButton1 = current == 0;
35 | isAtButton2 = current == numPins - 1;
36 | previous = current;
37 | Timer.pause(delay--);
38 | }
39 | }
40 |
41 | @Override
42 | public void handleButton1Pressed() {
43 | if (isAtButton1) {
44 | goesUp = true;
45 | }
46 | }
47 |
48 | @Override
49 | public void handleButton2Pressed() {
50 | if (isAtButton2) {
51 | goesUp = false;
52 | }
53 | }
54 |
55 | private void showScore() {
56 | System.out.println(points1 +" - "+ points2);
57 | // reset delay so new rally starts slow
58 | delay = 200;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/SevenLedsTwoButtonsAlgorithm.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.InPin;
4 | import framboos.OutPin;
5 | import framboos.ReverseInPin;
6 | import framboos.algorithm.util.Timer;
7 |
8 | public abstract class SevenLedsTwoButtonsAlgorithm implements Algorithm {
9 |
10 | private final OutPin[] pins = new OutPin[7];
11 | private InPin button1;
12 | private InPin button2;
13 | private boolean isClosed = false;
14 |
15 | private boolean button1WasPressed = false;
16 | private boolean button2WasPressed = false;
17 |
18 | public void setUp() {
19 | for (int i = 0; i <= 6; i++) {
20 | pins[i] = new OutPin(i);
21 | }
22 | button1 = new InPin(8);
23 | button2 = new InPin(9);
24 | new Thread() {
25 | public void run() {
26 | while (!isClosed) {
27 | checkButtons();
28 | Timer.pause(50);
29 | }
30 | }
31 | }.start();
32 | }
33 |
34 | public void tearDown() {
35 | for (OutPin pin : pins) {
36 | pin.close();
37 | }
38 | button1.close();
39 | button2.close();
40 | isClosed = true;
41 | }
42 |
43 | public void execute() {
44 | execute(pins);
45 | }
46 |
47 | private void checkButtons() {
48 | boolean isButton1Pressed = button1.getValue();
49 | boolean isButton2Pressed = button2.getValue();
50 | if ((isButton1Pressed && !button1WasPressed)) {
51 | handleButton1Pressed();
52 | }
53 | if ((isButton2Pressed && !button2WasPressed)) {
54 | handleButton2Pressed();
55 | }
56 | button1WasPressed = isButton1Pressed;
57 | button2WasPressed = isButton2Pressed;
58 | }
59 |
60 | public abstract void execute(OutPin[] pins);
61 |
62 | public abstract void handleButton1Pressed();
63 |
64 | public abstract void handleButton2Pressed();
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/framboos/algorithm/NineLedsTwoButtonsAlgorithm.java:
--------------------------------------------------------------------------------
1 | package framboos.algorithm;
2 |
3 | import framboos.InPin;
4 | import framboos.OutPin;
5 | import framboos.algorithm.util.Timer;
6 |
7 | public abstract class NineLedsTwoButtonsAlgorithm implements Algorithm {
8 |
9 | private final OutPin[] leds = new OutPin[9];
10 | private InPin button1;
11 | private InPin button2;
12 | private boolean isClosed = false;
13 |
14 | private boolean button1WasPressed = false;
15 | private boolean button2WasPressed = false;
16 |
17 | public void setUp() {
18 | for (int i = 0; i <= 7; i++) {
19 | leds[i] = new OutPin(i);
20 | }
21 | leds[8] = new OutPin(10);
22 | button1 = new InPin(8);
23 | button2 = new InPin(9);
24 | new Thread() {
25 | public void run() {
26 | while (!isClosed) {
27 | checkButtons();
28 | Timer.pause(50);
29 | }
30 | }
31 | }.start();
32 | }
33 |
34 | public void tearDown() {
35 | for (OutPin pin : leds) {
36 | pin.close();
37 | }
38 | button1.close();
39 | button2.close();
40 | isClosed = true;
41 | }
42 |
43 | public void execute() {
44 | lightLeds(leds);
45 | }
46 |
47 | private void checkButtons() {
48 | boolean isButton1Pressed = button1.getValue();
49 | boolean isButton2Pressed = button2.getValue();
50 | if ((isButton1Pressed && !button1WasPressed)) {
51 | handleButton1Pressed();
52 | }
53 | if ((isButton2Pressed && !button2WasPressed)) {
54 | handleButton2Pressed();
55 | }
56 | button1WasPressed = isButton1Pressed;
57 | button2WasPressed = isButton2Pressed;
58 | }
59 |
60 | public OutPin[] getLeds() {
61 | return leds;
62 | }
63 |
64 | public abstract void lightLeds(OutPin[] pins);
65 |
66 | public void handleButton1Pressed() {
67 | // default no action
68 | }
69 |
70 | public void handleButton2Pressed() {
71 | // default no action
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/framboos/App.java:
--------------------------------------------------------------------------------
1 | package framboos;
2 |
3 | import java.util.ServiceLoader;
4 |
5 | import framboos.algorithm.Algorithm;
6 |
7 | /**
8 | * The main class
9 | *
10 | */
11 | public class App {
12 |
13 | private final Algorithm algorithm;
14 |
15 | public App(Algorithm algorithm) {
16 | this.algorithm = algorithm;
17 | }
18 |
19 | public void start() {
20 | System.out.println("Starting Framboos with algorithm "+algorithm.getClass().getSimpleName());
21 | algorithm.setUp();
22 | algorithm.execute();
23 | }
24 |
25 | public void stop() {
26 | System.out.println("\nGoodbye\n");
27 | algorithm.tearDown();
28 | }
29 |
30 | public static void main(String[] args) {
31 | Algorithm algorithm = args.length > 0 ? getAlgorithm(args[0]) : pickAlgorithm();
32 | final App app = new App(algorithm);
33 | Runtime.getRuntime().addShutdownHook(new Thread() {
34 | @Override
35 | public void run() {
36 | app.stop();
37 | }
38 | });
39 | app.start();
40 | }
41 |
42 | /**
43 | * silly random picker, improve later
44 | * @return
45 | */
46 | private static Algorithm pickAlgorithm() {
47 | Algorithm chosen = null;
48 | while (chosen == null) {
49 | for (Algorithm candidate : ServiceLoader.load(Algorithm.class)) {
50 | if (Math.random() < 0.001) {
51 | chosen = candidate;
52 | }
53 | }
54 | }
55 | return chosen;
56 | }
57 |
58 | private static Algorithm getAlgorithm(String name) {
59 | try {
60 | Class> matchingClass = Class.forName(name);
61 | if (matchingClass.isAssignableFrom(Algorithm.class)) {
62 | try {
63 | Algorithm algorithm = matchingClass.asSubclass(Algorithm.class).newInstance();
64 | return algorithm;
65 | } catch (Exception e) {
66 | throw new RuntimeException("Failed to instantiate algorithm class "+name);
67 | }
68 | }
69 | else {
70 | throw new RuntimeException("Class "+name+" is not a valid framboos.algorithm.Algorithm");
71 | }
72 | } catch (ClassNotFoundException e) {
73 | // Failed, now try service lookup
74 | }
75 | for (Algorithm candidate : ServiceLoader.load(Algorithm.class)) {
76 | if (candidate.getClass().getSimpleName().toLowerCase().matches(".*"+name.toLowerCase()+".*")) {
77 | return candidate;
78 | }
79 | }
80 | throw new RuntimeException("No algorithm found for name "+name);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/actor/SerialPortActor.scala:
--------------------------------------------------------------------------------
1 | package framboos.actor
2 |
3 | import akka.actor._
4 | import framboos._
5 | import scala.concurrent._
6 | import scala.async.Async.{ async, await }
7 | import ExecutionContext.Implicits.global
8 | import purejavacomm._
9 | import java.io._
10 | import CommonMessages._
11 |
12 | object SerialPortActor {
13 | def props(portName: String): Props = Props(new SerialPortActor(portName))
14 |
15 | /** Message to be sent over serial connection */
16 | case class SendMessage(message: String) extends Incoming
17 |
18 | /** Message received over serial connection */
19 | case class ReceiveMessage(message: String) extends Outgoing
20 | }
21 |
22 | class SerialPortActor(portName: String) extends Actor with ActorLogging {
23 |
24 | import SerialPortActor._
25 |
26 | var listeners = Set.empty[ActorRef]
27 |
28 | def receive = connecting
29 |
30 | connect
31 |
32 | def connect = async {
33 | findPort(portName) match {
34 | case Some(port) => {
35 | log.info(s"Port found: $portName")
36 | val in = new BufferedReader(new InputStreamReader(port.getInputStream))
37 |
38 | port.addEventListener(new SerialPortEventListener {
39 | def serialEvent(event: SerialPortEvent) {
40 | if (event.getEventType == SerialPortEvent.DATA_AVAILABLE) {
41 | log.debug("New serial input")
42 | while (in.ready) {
43 | val nextLine = in.readLine
44 | log.debug(s"Receiving message: $nextLine")
45 | listeners foreach { _ ! ReceiveMessage(nextLine) }
46 | }
47 | }
48 | }
49 | })
50 |
51 | val out = new BufferedWriter(new OutputStreamWriter(port.getOutputStream))
52 | out.write("Hello from SerialPortActor\n")
53 | out.flush
54 | context.become(connected(in, out), true)
55 | }
56 | case None => {
57 | log.error(s"Could not find port $portName")
58 | }
59 | }
60 | }
61 |
62 | def findPort(portName: String): Option[SerialPort] = {
63 | import java.util._
64 | def findPort0(ports: Enumeration[CommPortIdentifier]): Option[SerialPort] = {
65 | if (ports.hasMoreElements) {
66 | val nextPortId: CommPortIdentifier = ports.nextElement
67 | if (nextPortId.getName().equalsIgnoreCase(portName)) {
68 | Some(openPort(nextPortId))
69 | } else {
70 | findPort0(ports)
71 | }
72 | } else {
73 | None
74 | }
75 | }
76 | findPort0(CommPortIdentifier.getPortIdentifiers.asInstanceOf[Enumeration[CommPortIdentifier]])
77 | }
78 |
79 | def openPort(portId: CommPortIdentifier): SerialPort = {
80 | val port: SerialPort = portId.open("SerialPortActor", 1000).asInstanceOf[SerialPort]
81 | port.notifyOnDataAvailable(true)
82 | port.setFlowControlMode(SerialPort.FLOWCONTROL_XONXOFF_IN + SerialPort.FLOWCONTROL_XONXOFF_OUT)
83 | port
84 | }
85 |
86 | import SerialPortActor._
87 |
88 | val connecting: Receive = {
89 | case AddListener(listener: ActorRef) => {
90 | listeners = listeners + listener
91 | }
92 | case RemoveListener(listener: ActorRef) => {
93 | listeners = listeners - listener
94 | }
95 | case SendMessage(message: String) => async {
96 | log.warning(s"Not connected, could not deliver message: $message")
97 | }
98 | }
99 |
100 | def connected(in: BufferedReader, out: BufferedWriter): Receive = {
101 | case AddListener(listener: ActorRef) => {
102 | listeners = listeners + listener
103 | }
104 | case RemoveListener(listener: ActorRef) => {
105 | listeners = listeners - listener
106 | }
107 | case SendMessage(message: String) => async {
108 | log.debug(s"Sending message: $message")
109 | out.write(message + '\n')
110 | out.flush
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/scala/framboos/GpioPin.scala:
--------------------------------------------------------------------------------
1 | package framboos;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.util.regex._;
7 |
8 | class GpioPin(pinNumber: Int, direction: Direction, isDirect: Boolean) {
9 |
10 | import GpioPin._
11 |
12 | var isClosing = false
13 |
14 | // export the pin, so it becomes available
15 | writeFile(exportPath, Integer.toString(pinNumber))
16 | // set the direction of the pin to either in or out
17 | writeFile(directionPath(pinNumber), direction.value)
18 |
19 | def value: Boolean = {
20 | if (isClosing) {
21 | false
22 | }
23 | try {
24 | val fis = new FileInputStream(valuePath(pinNumber))
25 | val read = fis.read
26 | fis.close
27 | read == '1'
28 | } catch {
29 | case e: IOException => {
30 | if (e.getMessage().contains("Permission Denied")) {
31 | throw new RuntimeException("Permission denied to GPIO file: " + e.getMessage())
32 | } else {
33 | throw new RuntimeException("Could not read from GPIO file: " + e.getMessage())
34 | }
35 | }
36 | }
37 | }
38 |
39 | // backward compatibility
40 | def getValue = value
41 |
42 | def close {
43 | isClosing = true
44 | writeFile(unexportPath, Integer.toString(pinNumber))
45 | }
46 |
47 | def writeFile(fileName: String, value: String) {
48 | try {
49 | // check for permission
50 | val fos = new FileOutputStream(fileName)
51 | fos.write(value.getBytes());
52 | fos.close();
53 | } catch {
54 | case e: IOException => {
55 | val msg = e.getMessage
56 | if (msg.contains("Permission denied")) {
57 | throw new RuntimeException("Permission denied to GPIO file: " + msg)
58 | }
59 | else if (msg.contains("busy")) {
60 | println("GPIO is already exported, continuing")
61 | }
62 | else {
63 | throw new RuntimeException("Could not write to GPIO file: " + msg)
64 | }
65 | }
66 | }
67 | }
68 | }
69 |
70 | sealed trait Direction {
71 | val value: String
72 | }
73 | case object In extends Direction {
74 | val value = "in"
75 | }
76 | case object Out extends Direction {
77 | val value = "out"
78 | }
79 |
80 | object GpioPin {
81 | // REV 1, left here for historic reasons:
82 | // val mappedPins: Array[Int] = Array(17, 18, 21, 22, 23, 24, 25, 4, 0, 1, 8, 7, 10, 9, 11, 14, 15)
83 | // REV 2:
84 | val mappedPins: Array[Int] = Array(17, 18, 27, 22, 23, 24, 25, 4, 2, 3, 8, 7, 10, 9, 11, 14, 15)
85 |
86 | val pinPattern: Pattern = Pattern.compile("(gpio)([0-9])_([0-9]*)", Pattern.CASE_INSENSITIVE)
87 | val pinPatternAlt: Pattern = Pattern.compile("(gpio)([0-9]*)", Pattern.CASE_INSENSITIVE)
88 | val pinPatternNumber: Pattern = Pattern.compile("([0-9]*)")
89 |
90 | val gpioPath = "/sys/class/gpio"
91 | val exportPath = gpioPath + "/export"
92 | val unexportPath = gpioPath + "/unexport"
93 | def devicePath(num: Int) = gpioPath + "/gpio%d" format num
94 | def directionPath(num: Int) = devicePath(num) + "/direction"
95 | def valuePath(num: Int) = devicePath(num) + "/value"
96 |
97 | def getPinNumber(pinName: String): Int = {
98 | val matcher = pinPattern.matcher(pinName)
99 | if (matcher.find) {
100 | Integer.parseInt(matcher.group(2)) * 32 + Integer.parseInt(matcher.group(3))
101 | } else {
102 | val matcherAlt = pinPatternAlt.matcher(pinName)
103 | if (matcherAlt.find) {
104 | Integer.parseInt(matcher.group(2))
105 | } else {
106 | val matcherAlt2 = pinPatternNumber.matcher(pinName)
107 | if (matcherAlt2.find) {
108 | Integer.parseInt(matcherAlt2.group(1))
109 | } else {
110 | throw new RuntimeException("Could not match " + pinName + ". As a valid gpio pinout number")
111 | }
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | framboos
6 | framboos
7 | 0.0.1-SNAPSHOT
8 | jar
9 |
10 | framboos
11 | http://maven.apache.org
12 |
13 |
14 | UTF-8
15 |
16 |
17 |
18 |
19 | com.sparetimelabs
20 | purejavacomm
21 | 0.0.21
22 |
23 |
24 | com.typesafe.akka
25 | akka-actor_2.11
26 | 2.3.6
27 |
28 |
29 | com.netflix.rxjava
30 | rxjava-scala
31 | 0.20.6
32 |
33 |
34 | org.scala-lang.modules
35 | scala-async_2.11
36 | 0.9.2
37 |
38 |
39 | org.scala-lang
40 | scala-library
41 | 2.11.2
42 |
43 |
44 |
45 |
46 | junit
47 | junit
48 | 3.8.1
49 | test
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | net.alchim31.maven
58 | scala-maven-plugin
59 | 3.1.6
60 |
61 |
62 |
63 |
64 |
65 | maven-assembly-plugin
66 |
67 |
68 | jar-with-dependencies
69 |
70 |
71 |
72 |
73 | net.alchim31.maven
74 | scala-maven-plugin
75 |
76 |
77 | scala-compile-first
78 | process-resources
79 |
80 | add-source
81 | compile
82 |
83 |
84 |
85 | scala-test-compile
86 | process-test-resources
87 |
88 | testCompile
89 |
90 |
91 |
92 |
93 |
94 | org.apache.maven.plugins
95 | maven-compiler-plugin
96 | 2.5.1
97 |
98 | 1.7
99 | 1.7
100 |
101 |
102 |
103 | org.apache.maven.plugins
104 | maven-jar-plugin
105 | 2.4
106 |
107 |
108 |
109 | framboos.App
110 | framboos
111 |
112 |
113 | development
114 | ${project.url}
115 |
116 |
117 |
118 |
119 |
120 | org.codehaus.mojo
121 | exec-maven-plugin
122 | 1.2.1
123 |
124 |
125 |
126 | java
127 |
128 |
129 |
130 |
131 | framboos.App
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | sparetimelabs
140 | http://www.sparetimelabs.com/maven2
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Java GPIO access
2 | ----------------
3 |
4 | Framboos is a small Java wrapper around the default GPIO driver on Linux boards like Raspberry Pi
5 | and BeagleBoard. It does not depend on any additional native libraries. The program that uses it
6 | must be run as root in order to access the driver files under /sys/class/gpio/. It supports input
7 | and output pins, as well as serial (UART) communication. It does not support PWM, SPI or I2C. If
8 | you need any of that, use [Pi4J](http://pi4j.com) instead, which includes native code and the
9 | wiringPi library. This is meant to be a pure Java (Scala) implementation that can be added as a
10 | simple Maven dependency to any Java project.
11 |
12 | Java code example to use an input pin:
13 |
14 | import framboos.InPin;
15 | InPin button = new InPin(8);
16 | boolean isButtonPressed = button.getValue();
17 | button.close();
18 |
19 | Java code example to use an output pin:
20 |
21 | import framboos.OutPin;
22 | OutPin led = new Outpin(0);
23 | led.setValue(true);
24 | led.close();
25 |
26 | Scala code example to use an input pin:
27 |
28 | import framboos.InPin
29 | val button = InPin(8)
30 | val isButtonPressed = button.value
31 | button.close
32 |
33 | Scala code example to use an output pin:
34 |
35 | import framboos.OutPin
36 | val led = Outpin(0)
37 | led.setValue(true)
38 | led.close
39 |
40 | It is important to close pins that are created, so that they are released for other programs.
41 |
42 | Extensions for Akka and asynchronous programming
43 | ------------------------------------------------
44 |
45 | Instead of creating and polling an input pin yourself, you can create an Observer that will run
46 | a function any time the pin value changes.
47 |
48 | Sample scala code to observe an input pin:
49 |
50 | import framboos.async.ObservableInPin
51 | val inPin = ObservableInPin(8)
52 | inPin.subscribe(newValue => println(s"New value $newValue")
53 |
54 | In an Akka application, you can create Actors that create GPIO/UART output based on received
55 | Akka messages, or that send Akka messages themselves on changed GPIO/UART input.
56 |
57 | WireUp is a provided sample Akka actor, that shows what the InPinActor, OutPinActor and
58 | SerialPortActor can do:
59 |
60 | val inPin8 = context.actorOf(Props(new InPinActor(8)), name = "inPin8")
61 | inPin8 ! AddListener(self)
62 |
63 | val outPin0 = context.actorOf(Props(new OutPinActor(0)), name = "outPin0")
64 |
65 | val serialPort = context.actorOf(Props(new SerialPortActor("ttyAMA0")), name = "serialPort")
66 | serialPort ! AddListener(self)
67 |
68 | def receive: Receive = {
69 | case NewValue(value: Boolean) => {
70 | outPin0 ! NewValue(value)
71 | val pressed = if (value) "pressed" else "released"
72 | serialPort ! SendMessage(s"Button $pressed at ${System.currentTimeMillis}\n")
73 | }
74 | case ReceiveMessage(message: String) => {
75 | outPin0 ! NewValue(true)
76 | serialPort ! SendMessage(s"Received your message: $message\n")
77 | }
78 | }
79 |
80 | InPinActor will send NewValue messages, containing a boolean of the new value. SerialPortActor
81 | will send ReceiveMessage messages for every line of text received on the serial port (which here
82 | is /dev/ttyAMA0, the default serial Rx/Tx pins on the GPIO header of the Raspberry Pi).
83 |
84 | OutPin will accept NewValue(boolean) messages, and set the output pin accordingly. SerialPortActor
85 | acccepts SendMessage messages as well, and will send the containing String over the serial line.
86 |
87 | Wired up like this, incoming serial input will be sent back with a prefix, and make the LED light
88 | up. Pressing the button will light the LED as well, and send a text over the serial line. Releasing
89 | the button will make the LED go off (even if it was triggered by incoming serial text).
90 |
91 | Pin assignment
92 | --------------
93 |
94 | When passing an integer to a constructor it will default to the wiringPi layout. When passing a
95 | String representation (i.e. "GPIO2_1" for BeagleBone) it will directly address the pin, bypassing
96 | the wiringPi numbering. Note that this library does not depend on or use wiringPi under the hood,
97 | but by default it uses its numbering scheme instead of e.g. the native Broadcom numbers in the
98 | case of Raspberry Pi.
99 |
100 | GPIO pins in the numbering used by wiringPi:
101 |
102 |
103 | | | |GND| | | 1 | | 4 | 5 | | 6 | 10| |
104 | |---|---|---|---|---|---|---|---|---|---|---|---|---|
105 | | | 8 | 9 | 7 | | 0 | 2 | 3 | | | | | |
106 |
107 | The same diagram as seen when the Pi is held upside down, with the GPIO headers towards you:
108 |
109 | | | | | | | 3 | 2 | 0 | | 7 | 9 | 8 | |
110 | |---|---|---|---|---|---|---|---|---|---|---|---|---|
111 | | | 10| 6 | | 5 | 4 | | 1 | | |GND| | |
112 |
113 | For simplicity sake, the assignments of the other pins are left out.
114 |
115 | Provided sample code
116 | --------------------
117 |
118 | Next to the wrapper classes, I made some classes that make connected LEDs light up in different
119 | patterns. For this to work, you need to connect the LEDs like I did. I use the Starter Kit from
120 | SK Pang, which contains a couple of LEDs, 2 buttons and 10 wires. At first I used 9 LEDs minus
121 | the number of buttons used, as the Starter Kit has limited wires, but later I decided to wire all
122 | 9 LEDs with custom wires, so there is no need to rewire when switching algorithms.
123 |
124 | For the Nine LED algorithms, connect pins 0 to 7 in that order to the LEDs, and 10 as ninth.
125 | Add button 1 to pin 8, and button 2 to pin 9. These pins have pull-up resistors to allow simple
126 | buttons that short-circuit when pressed.
127 |
128 | Usage
129 | -----
130 |
131 | To control the pins directly from your own code, simply add this dependency to your pom.xml:
132 |
133 |
134 | framboos
135 | framboos
136 | 0.0.1-SNAPSHOT
137 |
138 |
139 | And run this from the root of the framboos project:
140 |
141 | mvn install
142 |
143 | This will create a file target/framboos-X.Y.Z.jar and install it into your local repository.
144 |
145 | To run one of the patterns, run the application itself inside the jar:
146 |
147 | java -jar target/framboos-X.Y.Z.jar
148 |
149 | This will pick a random pattern to light up the LEDs. You can also specify the pattern you want
150 | to run, like this:
151 |
152 | java -jar target/framboos-X.Y.Z.jar caterpillar
153 |
154 | For a complete list of available pattern algorithms, examine this file:
155 |
156 | META-INF/services/framboos.algorithm.Algorithm
157 |
158 | Wiring notes
159 | ------------
160 |
161 | Please do use the resistors that come with the Starter Kit (or your own resistors if you don't
162 | use that kit) when connecting LEDs, or the current will fry them. Connect them serially, which
163 | means like this: connect one leg of the resistor to the - (minus) bottom line of the breadboard,
164 | where you also connected the ground of the Raspberry Pi. Connect the other side of the resistor
165 | with the short leg of the LED. Connect the long leg of the LED to whatever port you
166 | connect it to.
167 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------