├── .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 --------------------------------------------------------------------------------