Introduction: The Maven Box: an Arduino Controller for Software Developers

Every job has it's routine. I am a software developer who works with a Git/Maven based workspace everyday. So when I start working, my daily routine is to update and build my local workspace, pulling changes from GitHub, execute a maven build and execute the updated software. Usually I get my first coffee after that, but because I love coffee so much I thought there must be a faster way to get my system updated and running.

That's why I build the "Maven Box". It basically...

  • selects a branch from several GitHub projects
  • stashes changes before pulling
  • pulls the changes
  • triggers a maven build
  • shows the status of 6x test systems
  • has 3x customizeable function keys

What skills do you need? For whom was this instructable written?

For the Arduino stuff, only basic skills are required, especially if you only want to use buttons, switch buttons and LEDs. Components like the 74HC595 shift registers are a bit more complex, but with the help of the related documentation easy to handle. This instructable aims for Java developers who would like gain some experience with the Arduino. It does not explain in detail how to build this exact controller box but should give tips how to handle the major obstacles and where to find additional help.

Inspiration

I want to thank user travershenry who presented an awesome Arduino based game controller here at instructables.com (https://www.instructables.com/id/Lets-make-a-game-c...). It gave me a great inspiration when I build my controller and when writing this instructable.

Step 1: Gather Components

For the controller box you can use any switch, push button or LED available. I've build my box with the following components:

  • 1x Arduino Uno R3
  • USB Type B cable
  • 220 Ohm resistors
  • 10k Ohm resistors
  • some wires
  • 10x LED (3mm)
  • 4x push buttons
  • 3x switches
  • 1x rotary encoder
  • 1x 74HC595 (8 bit shift register)

The second list may differ depending on the components you want to use. I'm gonna show you within the next steps how to wire each component and how to write the corresponding code for it (or where to find it).

Step 2: Layout and Build Your Controller Case

Putting LEDs, switches and buttons into a box is not a big issue. Of course you want the box to look fancy. So I simply designed the background of the panel via Photoshop. I glued the paper on the top using regular paper glue and sealed it using "paper foil(?)" (a transparent gluey film you can use to wrap up book covers).

I drilled the holes for the buttons afterwards. Unfortunately I don't have a real workbench so not every hole was drilled precisely :-(

Finally, I copied the idea from user "travershenry" using carbon-fibre type decal for all sides (see link on top of this instructable).

Step 3: Wire Components

There are already enough tutorials out there for wiring LEDs, buttons and switches with an Arduino. So I skip the basic details here and provide links with the tutorials I've used instead:

Wiring LEDs

Arduino tutorial: https://www.arduino.cc/en/Tutorial/Blink

Wiring Switches

Arduino tutorial: https://www.arduino.cc/en/tutorial/switch

Button Debouncing

Arduino tutorial: https://www.arduino.cc/en/Tutorial/Debounce

Rotary Encoders

I've used the following encoder for my box:

https://www.conrad.de/de/encoder-5-vdc-001-a-schal...

I've used pin 2 and 3 for the encoder pins to use Arduino interrupts to read the single. The code for this is shown in the next step.

74HC595 8-bit Shift Register

Running out of digital pins? These shift registers allows you to control a big amount of LEDs with only 3 digital pins.

You can find a detailed tutorial about this component, including source code and wiring schemes here:

http://bildr.org/2011/02/74hc595/

Step 4: Implement the Arduino Sketch

With the help of the tutorials mentioned in the previous step, you should be able to write most of the Arduino sketch. I have attached here my full sketch that can be used as blueprint.

For the rotary encoder implementation, you will find different examples how to listen on the encoder changes.

I'm using the given implementation that works for the rotary encoder mentioned in the previous step:

//do not change these pins!
const int encoderPin1 = 3;
const int encoderPin2 = 2;
volatile int lastEncoded = 0;
int encoderValue = 0;


void setup(){
  Serial.begin(9600);  
  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on
  
  //call updateEncoder() when any high/low changed seen
  attachInterrupt(digitalPinToInterrupt(encoderPin1), updateEncoder, CHANGE); 
  attachInterrupt(digitalPinToInterrupt(encoderPin2), updateEncoder, CHANGE);
}

/**
 * The rotary encoder implementation
 */
void updateEncoder() {
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //LSB = least significant bit
  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; 

  if(sum == 2) {
    encoderValue --;
    String value = String(encoderValue);
    String cmd = "{source:'ROTARY_ENCODER', event:'ROTATE_LEFT'}";
    Serial.println(cmd);
  }

  if(sum == 1) {
    encoderValue ++;
    String value = String(encoderValue);
    String cmd = "{source:'ROTARY_ENCODER', event:'ROTATE_RIGHT'}";
    Serial.println(cmd);
  }
  lastEncoded = encoded; //store this value for next time  
}

The next section explains how the bi-directional communication is established with a Java program running on the computer the Arduino is connected with.

Step 5: Bi-Directional Communication Between Arduino and Java

The basic idea of the whole project was to have a Java daemon running on my windows machine that will

  • receive messages from the Arduino
  • display these messages via pop-up windows
  • send information to the Arduino with monitoring information
  • update the status of some LEDs to visualize this information

Let's have a look on the communication directions and how they can be implemented:

Sending messages from the Arduino to Java

As you've seen in the previous step the Arduino sends strings in JSON format to the serial port:

String cmd = "{source:'ROTARY_ENCODER', event:'ROTATE_RIGHT'}";
Serial.println(cmd);

The serial communication of the Arduino is setup in the init method of the sketch. You can use any machine readable format you prefer. I've used JSON since the conversion to a Pojo is pretty easy using GSON.

Receive messages from the Arduino

I've started the project using the rxtx library for Java (http://fizzed.com/oss/rxtx-for-java). I was able to send and receive messages from/to the Arduino, but I wasn't able to listen on messages from the Arduino in a non-blocking way. As a result my program was running with bigger CPU usage than necessary, so I switched to jSerialComm (http://fazecast.github.io/jSerialComm/) - a platform-independent serial port access for Java which supports event-based reading and writing via callbacks.

Establishing the connection with jSerialComm:

SerialPort serialPort = SerialPort.getCommPort(port);
serialPort.setBaudRate(9600);
serialPort.openPort();

The port is the COM port the Arduino is connected on. If you don't know which port it is, open the windows device manager - it will show you what port is used (see screenshot).

Listen on messages from the Arduino:

serialPort.addDataListener(new SerialPortDataListener() {

  @Override
  public int getListeningEvents() {
    return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
  }

  @Override
  public void serialEvent(SerialPortEvent event) {
    if(event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
      return;
    }
    byte[] newData = new byte[serialPort.bytesAvailable()];
    serialPort.readBytes(newData, newData.length);

    String arduinoMessage = new String(newData);

    ....
  }
});

Parsing the JSON message from the serial events is actually a little bit more difficult. Have a look at the sources at GitHub (https://github.com/syd711/mephisto_vi). The class ArduinoClient.java implements the communication with the Arduino and the JSON parsing and object creation via GSON.

Sending messages from Java to the Arduino

My control panel has 6x LEDs which are indicating the availability of test-systems. The Java program checks the availability via HTTP requests and updates the status of the LEDs depending on the return code of the HTTP request.

The status string with this monitoring information is written to the serial output stream of the Arduino connection:

if(serialPort.isOpen()) {
  output = new BufferedOutputStream(serialPort.getOutputStream());
  String monitoringResult = "monitoring:0:true|";
  output.write(monitoringResult.getBytes());
  output.flush();
}

In this example the monitoringResult string "monitoring:0:true|" indicates that the first pipeline that is monitored is available. The suffix "|" indicates the end of the command that is written to the Arduino. Now the Arduino has to parse this string:

Receiving messages from Java

The following method is called inside the loop method of the Arduino sketch:

void listenForUpdates() {
  String command = "";
  if(Serial.available() > 0) {
    command = Serial.readStringUntil('|');    
  }
  if(command.length() > 0) {
    String commandToken = tokenize(command, ':', 0);
    
    if(commandToken == CMD_MONITORING) {
      int index = tokenize(command, ':', 1).toInt();
      String statusToken = tokenize(command, ':', 2);
      boolean enabled = statusToken == "true";

      registerState[index-1] = enabled;
      Serial.println("{'message':'" + command + "'}");
    }
  }
}

The reading of the serial messages is based on the method Serial.readStringUntil('|'). The pipe symbol indicates the end of a command so that the command itself can be analyzed and evaluated. In this example the parsing of the monitoring command is shown that will update the array registerState with the current status of the pipline at the position index.

Step 6: Designing the User Interface

Once the communication between the Arduino and the Java program is established successfully, you may want to use the buttons on your control panel to visualize information on the computer's screen. For my control panel, I have three different situations where I want to show a notification or control UI:

  • Show the status of all test pipelines when the "pipeline" button is pressed.
  • Show a notification for n seconds when one of the switch button was switched.
  • Show a project control slider when the rotary encoder of the push button is pressed.

I've attached some screenshots with comments that visualize how the UI is working. I've implemented the UI with JavaFX, feel free to use Swing, AWT or SWT instead (if you even need a UI).

The sources are hosted at GitHub: https://github.com/syd711/mephisto_vi

Step 7: Autostart the Java Program in the Background

There are a lot of different solutions out there to run a Java program as a windows service. I've googled some, tried some, failed and finally used winrun4j (http://winrun4j.sourceforge.net/). The setup was done in a few minutes and I was totally satisfied with putting a windows link into the autostart folder instead of configuring a real service.

According to stackoverflow.com the Apache Commons Daemon projects (http://commons.apache.org/proper/commons-daemon/in...) seems the way to go, but I wasn't able to properly setup my project for this.

Step 8: Conclusion

What I loved about the project was the variety of work: to design and photoshop the controller layout, drill and tape the case, think about the required buttons and switches and to program the Arduino and Java frontend + backend.

And of course, here is the list of issues I would do different the next time:

  • Take more care about the size of the case: I had to re-solder most of the wires since I wasn't able to put the wired plate back into the case. The jumper wires I've put into the Arduino were simply too high.
  • Drilling holes: I already mentioned that I could have drilled the holes more precisely - my number one pain point.
  • The lamination problem: Maybe I should have drilled the holes after I've glued the controller background and before I've laminated it. This would have avoided the nasty laminate rips.

I hope you enjoyed reading this instructable and found some inspirations for you next Arduino project.