Thursday Aug 16, 2012

JavaFX Interface For Power Control

Power Control JavaFX Interface
Having completed the construction of my power control system I've finally found time to build the software interface using the Arduino board I included in it.

First off I needed some code on the Arduino that would listen for commands comming via the USB connection and then take the appropriate action.  Since all that is required is to set one of two pins either high or low the protocol is trivial.  The C code for the Arduino is shown below:
#define SOCKET_PIN_1 3
#define SOCKET_PIN_2 2
#define SOCKET_1_ON 65
#define SOCKET_1_OFF 97
#define SOCKET_2_ON 66
#define SOCKET_2_OFF 98

void setup(){
  pinMode(SOCKET_PIN_1, OUTPUT);
  pinMode(SOCKET_PIN_2, OUTPUT);
 
  Serial.begin(9600);
  Serial.println("READY");
}

void loop(){
  int incomingByte = 0;
 
  /* Wait for control command from the PC */
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
 
    switch (incomingByte) {
      case SOCKET_1_ON:
        digitalWrite(SOCKET_PIN_1, HIGH);
        Serial.println("Socket 1: ON");
        break;
      case SOCKET_1_OFF:
        digitalWrite(SOCKET_PIN_1, LOW);
        Serial.println("Socket 1: OFF");
        break;
      case SOCKET_2_ON:
        digitalWrite(SOCKET_PIN_2, HIGH);
        Serial.println("Socket 2: ON");
        break;
      case SOCKET_2_OFF:
        digitalWrite(SOCKET_PIN_2, LOW);
        Serial.println("Socket 2: OFF");
        break; 
    }
  }
}
All this does is initialise the serial port to work at 9600 baud and configure pins 2 and 3 as outputs.  Why use pins 2 and 3 and not 0 and 1 I hear you ask.  The answer is that pins 0 and 1 are also used for accessing the UART of the Arduino.  Once programmed and up and running this is no problem, but if you have an application running that is using these pins you can't then upload a program the Arduino though the USB port.  This caused me some problems to start with until I found a blog reference to this elsewhere.  To make life easier I switched to using pins 2 and 3.

The loop function looks for bytes being sent via the USB serial connection and takes the appropriate action in setting the pins high or low.  To keep things simple I used 'a' and 'A' for socket 1 and 'b' and 'B' for socket 2.  Lower case sets the pins low (turning the socket off) and upper case sets the pin high (turning the socket on).  To test this all you need to do is use the Serial Monitor in the Arduino IDE and type the appropriate character.

Next we need some way of sending the appropriate bytes from the controlloing PC.  Java has long had the JavaComm API which provides an API for all things serial and parallel.  The PC I'm using for the UI is running Ubuntu Linux, so I used the available librxtx-java package.  This has a rather frustrating limitation, that I would describe as a bug.  Plugging the Arduino USB into my machine automatically creates me a device to use to access this, which is what we need.  In this case the device is /dev/ttyACM0.  The problem is that librxtx-java will only recognise serial ports of the form /dev/ttyS{number}.  To get round this I created a symbolic link from /dev/ttyACM0 to /dev/ttyS4 (since I actually have physical serial ports on my machine using ttyS0 to ttyS3).  The big drawback to this is that when the machine is rebooted the OS very thoughtfully removes my symbolic link.  At some point I need to try and figure out if there is a way through udev to make this work properly.

The code below shows part of the class I created to handle communication with the Arduino through the serial port:
 public ArduinoComms(String portName) throws ArduinoCommsException {
   debug("AC: opening port: " + portName);
   CommPortIdentifier portIdentifier = null;
   CommPort commPort = null;

   try {
     portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
     debug("AC: Got portIdentifier");
   } catch (NoSuchPortException ex) {
     debug("AC: getPortIdentifier failed");
     throw new ArduinoCommsException(ex.getMessage());
   }

   if (portIdentifier.isCurrentlyOwned())
     throw new ArduinoCommsException("Error: Port is currently in use");
   else {
     try {
       commPort = portIdentifier.open(this.getClass().getName(), 2000);
       debug("AC: Opened port");
 
       if (commPort instanceof SerialPort) {
         SerialPort serialPort = (SerialPort) commPort;
         serialPort.setSerialPortParams(9600,
         SerialPort.DATABITS_8,
         SerialPort.STOPBITS_1,
         SerialPort.PARITY_NONE);
         debug("AC: Set parameters");

         in = serialPort.getInputStream();
         out = serialPort.getOutputStream();
         debug("AC: Got input/output streams");
       } else {
         System.out.println("ERROR: Not recognised as a serial port!" );
         throw new ArduinoCommsException(portName);
       }
     } catch (PortInUseException |
              UnsupportedCommOperationException |
              IOException ex) {
       throw new ArduinoCommsException(ex.getMessage());
     }
   }
 }
Passing /dev/ttyS4 to this constructor provides the application with an InputStream and OutputStream to communicate with the Arduino.  To simplify things further I subclassed my Arduino communications class to make it specific to my power control adding some useful methods shown below:
 /**
  * Turn socket one on
  * 
  * @throws IOException If this fails
  */
 public void socketOneOn() throws IOException {
   debug("PC: socketOneOn");
 
   if (out != null)
     out.write(SOCKET_ONE_ON);
   else
     throw new IOException("Output stream is null!");
 }

 /**
  * Turn socket one off
  * 
  * @throws IOException If this fails
  */
 public void socketOneOff() throws IOException {
   debug("PC: socketOneOff");
 
   if (out != null)
     out.write(SOCKET_ONE_OFF);
   else
     throw new IOException("Output stream is null!");
 }

 /**
  * Turn socket two on
  * 
  * @throws IOException If this fails
  */
 public void socketTwoOn() throws IOException {
   debug("PC: socketTwoOn");
 
   if (out != null)
     out.write(SOCKET_TWO_ON);
   else
     throw new IOException("Output stream is null!");
 }

 /**
  * Turn socket two off
  * 
  * @throws IOException If this fails
  */
 public void socketTwoOff() throws IOException {
   debug("PC: socketTwoOff");
 
   if (out != null)
     out.write(SOCKET_TWO_OFF);
   else
     throw new IOException("Output stream is null!");
 }

All that is the required now is a user interface to provide a way of sending the appropriate character when the user wants to change the power state.  I borrowed some button graphics from Jaspers JavaOne Kinect demo last year and a nice background I found here.  The result is shown below:
screen shot 1

screen shot 2

screen shot 3

The code for the JavaFX part is shown below:

 
    /**
     * Background
     */
    URL resourceURL = PowerUI.class.getResource("resources/background.png");
    Image backgroundImage = new Image(resourceURL.toExternalForm());
    ImageView background = new ImageView(backgroundImage);
    getChildren().add(background);
    
    /**
     * Images for switches
     */
    resourceURL = PowerUI.class.getResource("resources/power-off.png");
    Image powerOffImage = new Image(resourceURL.toExternalForm());
    resourceURL = PowerUI.class.getResource("resources/power-on.png");
    Image powerOnImage = new Image(resourceURL.toExternalForm());

    final ImageView powerOffSocketA = new ImageView(powerOffImage);
    final ImageView powerOnSocketA = new ImageView(powerOnImage);
    final ImageView powerOffSocketB = new ImageView(powerOffImage);
    final ImageView powerOnSocketB = new ImageView(powerOnImage);

    Font f = new Font(18);
    
    /**
     * Label and control for the first socket
     */
    Group labelA = GroupBuilder.
        create().
        translateX(35).
        translateY(20).
        build();
    Rectangle r = RectangleBuilder.
        create().
        width(120).
        height(30).
        fill(Color.YELLOW).
        build();
    labelA.getChildren().add(r); 
    Text socketALabel = TextBuilder.
        create().
        text("POWER 1").
        font(f).
        fill(Color.BLACK).
        translateX(18).
        translateY(21).
        build();
    labelA.getChildren().add(socketALabel);
    getChildren().add(labelA);
    
    powerOffSocketA.setTranslateX(60);
    powerOffSocketA.setTranslateY(70);
    powerOffSocketA.setOnMouseClicked(new EventHandler() {
      @Override
      public void handle(MouseEvent t) {
        powerOffSocketA.setVisible(false);
        powerOnSocketA.setVisible(true);
        
        try {
          debug("PUI: Socket 1 ON");
          comms.socketOneOn();
        } catch (IOException ex) {
          System.out.println("ERROR: " + ex.getMessage());
        }
      }
    });
    getChildren().add(powerOffSocketA);

    powerOnSocketA.setTranslateX(60);
    powerOnSocketA.setTranslateY(70);
    powerOnSocketA.setOnMouseClicked(new EventHandler() {
      @Override
      public void handle(MouseEvent t) {
        powerOffSocketA.setVisible(true);
        powerOnSocketA.setVisible(false);
        
        try {
          debug("PUI: Socket 1 OFF");
          comms.socketOneOff();
        } catch (IOException ex) {
          System.out.println("ERROR: " + ex.getMessage());
        }
      }
    });
    powerOnSocketA.setVisible(false);
    getChildren().add(powerOnSocketA);

    /**
     * Label and control for the first socket
     */
    Group labelB = GroupBuilder.
        create().
        translateX(190).
        translateY(20).
        build();
    r = RectangleBuilder.
        create().
        width(120).
        height(30).
        fill(Color.YELLOW).
        build();
    labelB.getChildren().add(r); 
    Text socketBLabel = TextBuilder.
        create().
        text("POWER 2").
        font(f).
        fill(Color.BLACK).
        translateX(18).
        translateY(21).
        build();
    labelB.getChildren().add(socketBLabel);
    getChildren().add(labelB);
    
    powerOffSocketB.setTranslateX(215);
    powerOffSocketB.setTranslateY(70);
    powerOffSocketB.setOnMouseClicked(new EventHandler() {
      @Override
      public void handle(MouseEvent t) {
        powerOffSocketB.setVisible(false);
        powerOnSocketB.setVisible(true);
        
        try {
          debug("PUI: Socket 2 ON");
          comms.socketTwoOn();
        } catch (IOException ex) {
          System.out.println("ERROR: " + ex.getMessage());
        }
      }
    });
    getChildren().add(powerOffSocketB);
    powerOnSocketB.setTranslateX(215);
    powerOnSocketB.setTranslateY(70);
    powerOnSocketB.setOnMouseClicked(new EventHandler() {
      @Override
      public void handle(MouseEvent t) {
        powerOffSocketB.setVisible(true);
        powerOnSocketB.setVisible(false);
        
        try {
          debug("PUI: Socket 2 OFF");
          comms.socketTwoOff();
        } catch (IOException ex) {
          System.out.println("ERROR: " + ex.getMessage());
        }
      }
    });
    powerOnSocketB.setVisible(false);
    getChildren().add(powerOnSocketB);
One of the things I've just started really using when developing JavaFX is the Builder classes.  These are great for making it easy to create Nodes and setting numerous attributes without having to call each method individually on the object.

I guess the next thing is to make this into a simple web service so I can control my Raspberry Pi and Beagle Board from a web browser antwhere in the world.

About

A blog covering aspects of Java SE, JavaFX and embedded Java that I find fun and interesting and want to share with other developers. As part of the Developer Outreach team at Oracle I write a lot of demo code and I use this blog to highlight useful tips and techniques I learn along the way.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today