Handling Arduino Events in Java
In my previous blog, I showed how to write a Java application to run on a host computer, which interfaces with a sketch running on an Arduino to turn an attached light on and off. Someone asked if it's possible to handle continuous events from an Arduino, and the answer is yes. Let's see how.
Arduino Event Handler
I've been using RXTX for the communication between the Arduino and Java over a serial connection. In my next blog, I plan to explore some alternatives, but let's stick to RXTX for now. I won't review too much here, but after connecting to the Arduino (details in a previous blog), you set up your event listener with two lines of code:
// Add event listener serialPort.addEventListener(this); serialPort.notifyOnDataAvailable(true);
You need to pass a reference to a class that implements the gnu.io.SerialPortEventListener
, which includes just one method: serialEvent()
. A SerialPortEvent
object is passed as the lone parameter. Implementing this method is straightforward; just check the event type for DATA_AVAILABLE
and process the data by reading from the serial port's InputStream
. You need to set this up with one line of code prior to the event like this:
// Setup input reader for incoming data from Arduino input = new BufferedReader( new InputStreamReader( serialPort.getInputStream() ));
Temperature Sensor
To make the event processing interesting, I'm using a temperature sensor I bought (along with many others) from ManyLabs. My kit came with a shield that allows multiple analog and digital sensors to be connected to an Arduino. I placed the shield on my Arduino, plugged the sensor into port S1, and wrote a simple sketch that publishes the sensor readings over the serial port. Let's walk through this:
int tempSensorPin = 0; void setup() { Serial.begin(9600); //Start the serial connection with the computer //to view the result open the serial monitor } void loop() { //getting the voltage reading from the temperature sensor int tempSensorVal = analogRead(tempSensorPin); // Formula for converting voltage to temp in C const int B=3975; float Rsensor = (float)(1023-tempSensorVal)*10000/tempSensorVal; double TEMP = 1 / (log(Rsensor/10000)/B+1/298.15)-273.15; // now convert to Fahrenheit float temperatureF = (TEMP * 9.0 / 5.0) + 32.0; Serial.print(temperatureF); Serial.println(" degrees F"); delay(1000); //waiting a second }
The sensor works by generating a voltage reading that varies by temperature. The trick is to take this reading, convert it to a temperature using a formula (it's simpler than it may appear), and then convert to either Celsius or Fahrenheit. To "publish" this as an event over the serial port, simply use Serial.print
and/or Serial.println
, assuming you've initialized the serial port as I have in this example.
You should be able to verify your temperature reading by using the Arduino Serial Monitor to view the output, which is sent once per second.
The Java Side: Using JavaFX to Show the Temperature
I thought it would be neat to show the temperature via a fancy JavaFX user interface. I found a nice set of JavaFX gauges as part of the SteelFX project thanks to Jim Clarke and the JFXtras Labs. I believe Gerrit Grunwald was one of the main contributors to this gauge, although the lab's home page includes a long list of contributors.
To show the temperature, I used the code that Jim Clarke wrote about, which leverages FXML for the gauge definition. I modified the FXML file only slightly to set the temperature units and the class name within my project. After creating a JavaFX project, I modified the start()
method as below:
@Override public void start(Stage primaryStage) throws Exception { // Use FXML to get the panel and the child radial gauge Parent panel = FXMLLoader.load( this.getClass().getResource("/steelfx/Gauge.fxml")); if ( radialGauge == null ) { ObservableList<Node> children = panel.getChildrenUnmodifiable(); radialGauge = (Radial)children.get(0); } // Setup the scene and stage and display it StackPane root = new StackPane(); root.getChildren().add(panel); Scene scene = new Scene(root, 300, 300); scene.setFill(Color.web("#333")); primaryStage.setTitle("Arduino Temperature Guage"); primaryStage.setScene(scene); primaryStage.show(); // Now start up the Arduino communication initArduino(); }
The initArduino()
method is from my previous blog. The only other place where I needed to add any specific temperature processing and display code was in the method that handles the Arduino events:
@Override public void serialEvent(SerialPortEvent spe) { try { switch (spe.getEventType() ) { case SerialPortEvent.DATA_AVAILABLE: String inputLine = input.readLine(); int end = inputLine.indexOf("degrees"); if ( end > 0 ) { String temp = inputLine.substring(0, end); radialGauge.setValue(new Double(temp)); } break; default: break; } } catch (Exception e) { System.err.println(e.toString()); } }
This code parses the temperature value from the published String
, and sets the gauge's value accordingly. That's it! As the temperature changes (which you can force by holding or blowing on the temperature sensor), the gauge will update dynamically as well. Click here for the full listing.
Happy coding!
-EJB