JavaFX on Raspberry Pi: GPIO Controller (Part2)

Part1 of this little series of articles was about the hardware setup of my little Raspberry Pi GPIO project.
Now let’s focus the software.

The Schema

RaspiGpioControllSchema

GpioAdapterFX

For the bridge between Java and the Raspberry’s GPIO I took advantage of the great work of the Pi4J Project guys.
The com.pi4j.io.gpio.GpioController is a straighforward and simple wrapper to manage the Pi4J GPIOController with JavaFX Properties:


public class GpioAdapterFX{

  private GpioController GPIO;
  private ObservableMap<BooleanProperty, ChangeListener<Boolean>> pins;
  private BooleanProperty pin0StateProperty;
  private BooleanProperty pin1StateProperty;
  private BooleanProperty pin2StateProperty;
  private BooleanProperty pin3StateProperty;
  private BooleanProperty pin4StateProperty;
  private BooleanProperty pin5StateProperty;
  private BooleanProperty pin6StateProperty;
  private BooleanProperty pin7StateProperty;
  private Timeline testTimeline;

  public CpioFXAdapter() {
    init();
  }

 private void init() {
    pin0StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin1StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin2StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin3StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin4StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin5StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin6StateProperty = new SimpleBooleanProperty(Boolean.FALSE);
    pin7StateProperty = new SimpleBooleanProperty(Boolean.FALSE);

    pins = FXCollections.observableHashMap();
    pins.put(pin0StateProperty, null);
    pins.put(pin1StateProperty, null);
    pins.put(pin2StateProperty, null);
    pins.put(pin3StateProperty, null);
    pins.put(pin4StateProperty, null);
    pins.put(pin5StateProperty, null);
    pins.put(pin6StateProperty, null);
    pins.put(pin7StateProperty, null);
  }

 private ChangeListener<Boolean> createPinListener(final GpioPinDigitalOutput pin) {
    return new ChangeListener<Boolean>() {
      @Override
      public void changed(ObservableValue<? extends Boolean> ov, Boolean oldValue, Boolean newValue) {
        if (newValue) {
          pin.high();
        } else {
          pin.low();
        }
      }
    };
  }

  /*
   * -------------------------- ACTIONS -------------------------- 
   */  
  public void connect() {

    GPIO = GpioFactory.getInstance();

     GpioPinDigitalOutput pin0 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_00, "PIN0", PinState.LOW);
    GpioPinDigitalOutput pin1 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_01, "PIN1", PinState.LOW);
    GpioPinDigitalOutput pin2 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_02, "PIN2", PinState.LOW);
    GpioPinDigitalOutput pin3 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_03, "PIN3", PinState.LOW);
    GpioPinDigitalOutput pin4 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_04, "PIN4", PinState.LOW);
    GpioPinDigitalOutput pin5 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_05, "PIN5", PinState.LOW);
    GpioPinDigitalOutput pin6 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_06, "PIN6", PinState.LOW);
    GpioPinDigitalOutput pin7 = GPIO.
            provisionDigitalOutputPin(RaspiPin.GPIO_07, "PIN7", PinState.LOW);


    pins.put(pin0StateProperty, createPinListener(pin0));
    pins.put(pin1StateProperty, createPinListener(pin1));
    pins.put(pin2StateProperty, createPinListener(pin2));
    pins.put(pin3StateProperty, createPinListener(pin3));
    pins.put(pin4StateProperty, createPinListener(pin4));
    pins.put(pin5StateProperty, createPinListener(pin5));
    pins.put(pin6StateProperty, createPinListener(pin6));
    pins.put(pin7StateProperty, createPinListener(pin7));

    pin0StateProperty.addListener(pins.get(pin0StateProperty));
    pin1StateProperty.addListener(pins.get(pin1StateProperty));
    pin2StateProperty.addListener(pins.get(pin2StateProperty));
    pin3StateProperty.addListener(pins.get(pin3StateProperty));
    pin4StateProperty.addListener(pins.get(pin4StateProperty));
    pin5StateProperty.addListener(pins.get(pin5StateProperty));
    pin6StateProperty.addListener(pins.get(pin6StateProperty));
    pin7StateProperty.addListener(pins.get(pin7StateProperty));

    reset();
  }

 public void disconnect() {
    if (GPIO != null) {
      GPIO.shutdown();
    }
    for (BooleanProperty booleanProperty : pins.keySet()) {
      ChangeListener<Boolean> listener = pins.get(booleanProperty);
      if (listener != null) {
        booleanProperty.removeListener(listener);
      }
    }
  }

  public void setOnAllPins() {
    for (BooleanProperty booleanProperty : pins.keySet()) {
      booleanProperty.setValue(Boolean.TRUE);
    }
  }

  public void setOffAllPins() {
    for (BooleanProperty booleanProperty : pins.keySet()) {
      booleanProperty.setValue(Boolean.FALSE);
    }
  }

  public void reset() {
    if (testTimeline != null) {
      testTimeline.stop();
    }
    for (BooleanProperty booleanProperty : pins.keySet()) {
      booleanProperty.setValue(Boolean.FALSE);
    }
  }
 
}

SwitchBoardController

Finally the SwitchBoardController connects the UI to the GpioAdapterFX (bidirectional binding is my best friend).

To visualize the state of the GPIO Pins, I’m using Gerrit Grunwalds LED Control from his Enzo7 lib (a port of some of his JFXtras gauges to JavaFX 8).

public class SwitchBoardController implements Initializable {

  @FXML
  private GridPane buttonGridPane;
  @FXML
  private ToggleButton toogleGPIO0;
  @FXML
  private ToggleButton toogleGPIO1;
  @FXML
  private ToggleButton toogleGPIO2;
  @FXML
  private ToggleButton toogleGPIO3;
  @FXML
  private ToggleButton toogleGPIO4;
  @FXML
  private ToggleButton toogleGPIO5;
  @FXML
  private ToggleButton toogleGPIO6;
  @FXML
  private ToggleButton toogleGPIO7;
  @FXML
  private Button testButton;
  @FXML
  private Button resetButton;
  @FXML
  private Button exitButton;
  @FXML
  private ToggleButton gpioConnectButton;
  @FXML
  private ToggleGroup gpioConnectGroup;
  @FXML
  private ToggleButton gpioDisconnectButton;
  private GpioAdapterFX gpioAdapterFx;
  private Led indicatorGPIO0;
  private Led indicatorGPIO1;
  private Led indicatorGPIO2;
  private Led indicatorGPIO3;
  private Led indicatorGPIO4;
  private Led indicatorGPIO5;
  private Led indicatorGPIO6;
  private Led indicatorGPIO7;

  /**
   * Initializes the controller class.
   */
  @Override
  public void initialize(URL url, ResourceBundle rb) {
    assert buttonGridPane != null : "fx:id=\"buttonGridPane\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert exitButton != null : "fx:id=\"exitButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert gpioConnectButton != null : "fx:id=\"gpioConnectButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert gpioConnectGroup != null : "fx:id=\"gpioConnectGroup\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert gpioDisconnectButton != null : "fx:id=\"gpioDisconnectButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert resetButton != null : "fx:id=\"resetButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert testButton != null : "fx:id=\"testButton\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO0 != null : "fx:id=\"toogleCPIO0\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO1 != null : "fx:id=\"toogleCPIO1\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO2 != null : "fx:id=\"toogleCPIO2\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO3 != null : "fx:id=\"toogleCPIO3\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO4 != null : "fx:id=\"toogleCPIO4\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO5 != null : "fx:id=\"toogleCPIO5\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO6 != null : "fx:id=\"toogleCPIO6\" was not injected: check your FXML file 'SwitchBoard.fxml'.";
    assert toogleGPIO7 != null : "fx:id=\"toogleCPIO7\" was not injected: check your FXML file 'SwitchBoard.fxml'.";

    gpioAdapterFx = new GpioAdapterFX();

    indicatorGPIO0 = createIndicator();
    indicatorGPIO1 = createIndicator();
    indicatorGPIO2 = createIndicator();
    indicatorGPIO3 = createIndicator();
    indicatorGPIO4 = createIndicator();
    indicatorGPIO5 = createIndicator();
    indicatorGPIO6 = createIndicator();
    indicatorGPIO7 = createIndicator();

    buttonGridPane
            .add(indicatorGPIO0, 0, 0);
    buttonGridPane.add(indicatorGPIO1, 1, 0);
    buttonGridPane.add(indicatorGPIO2, 2, 0);
    buttonGridPane.add(indicatorGPIO3, 3, 0);
    buttonGridPane.add(indicatorGPIO4, 4, 0);
    buttonGridPane.add(indicatorGPIO5, 5, 0);
    buttonGridPane.add(indicatorGPIO6, 6, 0);
    buttonGridPane.add(indicatorGPIO7, 7, 0);

    indicatorGPIO0.onProperty().
            bindBidirectional(gpioAdapterFx.pin0StateProperty());
    indicatorGPIO1.onProperty().
            bindBidirectional(gpioAdapterFx.pin1StateProperty());
    indicatorGPIO2.onProperty().
            bindBidirectional(gpioAdapterFx.pin2StateProperty());
    indicatorGPIO3.onProperty().
            bindBidirectional(gpioAdapterFx.pin3StateProperty());
    indicatorGPIO4.onProperty().
            bindBidirectional(gpioAdapterFx.pin4StateProperty());
    indicatorGPIO5.onProperty().
            bindBidirectional(gpioAdapterFx.pin5StateProperty());
    indicatorGPIO6.onProperty().
            bindBidirectional(gpioAdapterFx.pin6StateProperty());
    indicatorGPIO7.onProperty().
            bindBidirectional(gpioAdapterFx.pin7StateProperty());

    toogleGPIO0.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin0StateProperty());
    toogleGPIO1.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin1StateProperty());
    toogleGPIO2.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin2StateProperty());
    toogleGPIO3.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin3StateProperty());
    toogleGPIO4.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin4StateProperty());
    toogleGPIO5.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin5StateProperty());
    toogleGPIO6.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin6StateProperty());
    toogleGPIO7.selectedProperty().
            bindBidirectional(gpioAdapterFx.pin7StateProperty());
  }

  private Led createIndicator() {
    return LedBuilder.create().
            prefHeight(80.0).
            prefWidth(80.0).
            build();
  }

  /*
   * -------------------------- ACTIONS -------------------------- 
   */
  @FXML
  public void testAction() {
    gpioAdapterFx.test();
  }

  @FXML
  public void resetAction() {
    gpioAdapterFx.reset();
  }

  @FXML
  public void gpioDisconnectAction() {
    gpioAdapterFx.disconnect();
  }

  @FXML
  public void gpioConnectAction() {
    gpioAdapterFx.connect();
  }

  @FXML
  public void exitAction() {
    gpioAdapterFx.reset();
    gpioAdapterFx.disconnect();
    Platform.exit();
  }
}

You can find the complete code here.

The runfxapp skript may help you to run the app on your Raspberry.

Stay tuned…

Inspired by Oliver Schneiders blog post I have already ordered the transmitter to control my wireless power outlets (but shipping from Hong Kong will take some weeks…).

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>