Sunday Aug 04, 2013

The Raspberry Pi JavaFX In-Car System (Part 4)

Raspberry Pi JavaFX Carputer part 4 It's been a while since my last blog entry about my in-car system, which has been due to a number of other things taking priority.  The good news is I now have more to report in terms of progress.

The first thing is that I decided to extend the scope of my project in terms of integrating with my vehicle.  Originally, I had planned to add a 7" touch screen somewhere that was visible whilst driving.  Given the attention to detail that Audi's designers have taken over the interior this was not going to be simple.  The company I had originally ordered the touchscreen from ran into production problems and after several months admitted that delivery of the screen would not be for "some time".  Since I needed this for JavaOne in September I cancelled the order and started looking for a replacement.  eBay is a great place to find items like this and I found a screen being marketed for the Raspberry Pi which was a "double DIN" fitting (which actually means it is twice the height of the ISO 7736 standard).  Some more searching on eBay turned up a bezel that would enable me to replace the existing navigation/entertainment system in my car with my new, Raspberry Pi powered one (Given how much functionality the existing system has I don't see this as a long term replacement, more for experimentation).

Having received my screen I decided that for development and testing it would be better if I did not need to keep changing the centre console, so I set about making the screen/Pi combination easier to use standalone.  Unfortunately, I couldn't find the perfect sized box at RS, but got one that could be adapted to my needs (the problem was it was too shallow, so I added some longer bolts and spacers).  First up was to fit the screen into the top of the box, as shown in the pictures

screen

screen

I was happy that my project already required the use of some wood, as I believe all great software projects should involve some woodwork.

To mount Raspberry Pi I used the two vacant mounting points on the screen and attached a small perspex sheet to act as a platform for the Pi

Pi mounting

Getting the holes in the right position took three attempts, as the positioning of the external cables was a bit tricky given the available space.

The Raspberry Pi was then mounted using the bolts shown above with some plastic spacers

Raspberry Pi mounted

The USB cables provided connections for a USB port and SD card reader which are part of the screen bezel.  In the end I removed these as I did not plan to use them and they were taking up too much space.

Fitting the HDMI cable was a bit of a challenge.  The distace between the HDMI port on the Pi and the one on the screen is about 3cm.  The shortest cable I had was 1m!  Using some cable ties and a sharp knife I was able to come up with a workable solution (not exactly pretty, but  it works and won't be seen in the finshed 'product').

HDMI cabling

Since I wanted to include an accelerometer I mounted that on the bottom of the box so it wouldn't move around during development.  The final internals are shown below.  I added a short ethernet extension lead to simplify cabled network access, the WiPi dongle could be left in place and I ran a USB extension lead from the Pi to simplify switching between the touch screen and an external keyboard.

internals

When assembled I had a pretty nifty looking Raspberry Pi computer

pi computer

In the next installment I'll cover how I started on the JavaFX part to deliver realtime data on the screen.


Friday Jun 28, 2013

The Raspberry Pi JavaFX In-Car System (Part 3)

Ras Pi car pt3 Having established communication between a laptop and the ELM327 it's now time to bring in the Raspberry Pi.

One of the nice things about the Raspberry Pi is the simplicity of it's power supply.  All we need is 5V at about 700mA, which in a car is as simple as using a USB cigarette lighter adapter (which is handily rated at 1A).  My car has two cigarette lighter sockets (despite being specified with the non-smoking package and therefore no actual cigarette lighter): one in the centre console and one in the rear load area.  This was convenient as my idea is to mount the Raspberry Pi in the back to minimise the disruption to the very clean design of the Audi interior.

The first task was to get the Raspberry Pi to communicate using Wi-Fi with the ELM 327.  Initially I tried a cheap Wi-Fi dongle from Amazon, but I could not get this working with my home Wi-Fi network since it just would not handle the WPA security no matter what I did.  I upgraded to a Wi Pi from Farnell and this works very well.

The ELM327 uses Ad-Hoc networking, which is point to point communication.  Rather than using a wireless router each connecting device has its own assigned IP address (which needs to be on the same subnet) and uses the same ESSID.  The settings of the ELM327 are fixed to an IP address of 192.168.0.10 and useing the ESSID, "Wifi327".  To configure Raspbian Linux to use these settings we need to modify the /etc/network/interfaces file.  After some searching of the web and a few false starts here's the settings I came up with:

auto lo eth0 wlan0

iface lo inet loopback

iface eth0 inet static
    address 10.0.0.13
    gateway 10.0.0.254
    netmask 255.255.255.0

iface wlan0 inet static
    address 192.168.0.1
    netmask 255.255.255.0
    wireless-essid Wifi327
    wireless-mode ad-ho0


After rebooting, iwconfig wlan0 reported that the Wi-Fi settings were correct.  However, ifconfig showed no assigned IP address.  If I configured the IP address manually using ifconfig wlan0 192.168.0.1 netmask 255.255.255.0 then everything was fine and I was able to happily ping the IP address of the ELM327.  I tried numerous variations on the interfaces file, but nothing I did would get me an IP address on wlan0 when the machine booted.  Eventually I decided that this was a pointless thing to spend more time on and so I put a script in /etc/init.d and registered it with update-rc.d.  All the script does (currently) is execute the ifconfig line and now, having installed the telnet package I am able to telnet to the ELM327 via the Raspberry Pi.  Not nice, but it works.

Here's a picture of the Raspberry Pi in the car for testing

In Car

In the next part we'll look at running the Java code on the Raspberry Pi to collect data from the car systems.






Wednesday Jun 12, 2013

The Raspberry Pi JavaFX In-Car System (Part 2)

Raspberry Pi JavaFX Car Pt2 In my last post (which was rather further back in time than I had planned) I described the ideas behind my in-car Raspberry Pi JavaFX system.  Now it's time to get started on the technical stuff.

First, we need a short review of modern car electronics.  Things have certainly moved on from my first car, which was a 1971 Mini Clubman.  This didn't even have electronics in it (unless you count the radio), as everything was electro-mechanical (anyone remember setting the gap for the points on the distributor?)  Today, in Europe at least, things like anti-lock brakes (ABS) and stability control (ESC) which require complex sensors and electronics are mandated by law.  Also, since 2001, all petrol driven vehicles have to be fitted with an EOBD (European On-Board Diagnostics) interface.  This conforms to the OBD-II standard which is where the ELM327 interface from my first blog entry comes in. 

As a standard, OBD-II mandates some parts while other parts are optional.  That way certain basic facilities are guaranteed to be present (mainly those that are related to the measuring of exhaust emission performance) and then each car manufacturer can implement the optional parts that make sense for the vehicle they're building. 

There are five signal protocols that can be used with the OBD-II interface:
  • SAE J1850 PWM (Pulse-width modulation, used by Ford)
  • SAE J1850 VPW (Variable pulse-width, used by General Motors)
  • ISO 9141-2 (which is a bit like RS-232)
  • ISO 14230
  • ISO 15765 (also referred to as Controller Area Network, or CAN bus)
You can think of this as the transport layer, which can be changed by the car manufacturer to suit their needs.  The message protocol which uses the signal protocol is defined by the OBD-II standard.  The format of these commands is pretty straightforward requiring a sequence  of pairs of hexadecimal digits.  The first pair indicates the 'mode' (of which there are 10); the second, and possibly third, pair indicates the 'parameter identification' or PID being sent.  The mode and PID combination defines the command that you are sending to the vehicle.  Results are returned as a sequence of bytes that form a string containing pairs of hexadecimal digits encoding the data.

For my current vehicle, which is an Audi S3, the protocol is ISO 15765 as the car has multiple CAN buses for communication between the various control units (we'll come back to this in more detail later).

So where to start?

The first thing that is necessary is to establish communication between a Java application and the ELM327.  One of the great things about using Java for an application like this is that the development can easily be done on a laptop and the production code moved easily to the target hardware.  No cross compilation tool chains needed here, thank you.

My ELM327 interface communicates via 802.11 (Wi-Fi).  The address of my interface is 192.168.0.11 (which seems pretty common for these devices) and uses port 35000 for all communication.  To test that things are working I set my MacBook to use a static IP address on Wi-Fi and then connected directly to the ELM327 which appeared in the list of available Wi-Fi devices.  Having established communication at the IP level I could then telnet into the ELM327.  If you want to start playing with this it's best to get hold of the documentation, which is really well written and complete.  The ELM327 essentially uses two modes of communication:
  • AT commands for talking to the interface itself
  • OBD commands that conform to the description above.  The ELM327 does all the hard work of  converting this to the necessary packet format, adding headers, checksums and so on as well as unmarshalling the  response data.
To start with I just used the AT I command which reports back the version of the interface and AT RV which gives the current car battery voltage.  These worked fine via telnet, so it was time to start developing the Java code. 

To keep things simple I wrote a class that would encapsulate the connection to the ELM327.  Here's the code that initialises the connection so that we can read and write bytes, as required

  /* Copyright © 2013, Oracle and/or its affiliates. All rights reserved. */

  private static final String ELM327_IP_ADDRESS = "192.168.0.10";
  private static final int ELM327_IP_PORT = 35000;
  private static final byte OBD_RESPONSE = (byte)0x40;
  private static final String CR = "\n";
  private static final String LF = "\r";
  private static final String CR_LF = "\n\r";
  private static final String PROMPT = ">";

  private Socket elmSocket;
  private OutputStream elmOutput;
  private InputStream elmInput;
  private boolean debugOn = false;
  private int debugLevel = 5;
  private byte[] rawResponse = new byte[1024];
  protected byte[] responseData = new byte[1024];

  /**
   * Common initialisation code
   *
   * @throws IOException If there is a communications problem
   */
  private void init() throws IOException {
    /* Establish a socket to the port of the ELM327 box and create
     * input and output streams to it
     */
    try {
      elmSocket = new Socket(ELM327_IP_ADDRESS, ELM327_IP_PORT);
      elmOutput = elmSocket.getOutputStream();
      elmInput = elmSocket.getInputStream();
    } catch (UnknownHostException ex) {
      System.out.println("ELM327: Unknown host, [" + ELM327_IP_ADDRESS + "]");
      System.exit(1);
    } catch (IOException ex) {
      System.out.println("ELM327: IO error talking to car");
      System.out.println(ex.getMessage());
      System.exit(2);
    }

    /* Ensure we have an input and output stream */
    if (elmInput == null || elmOutput == null) {
      System.out.println("ELM327: input or output to device is null");
      System.exit(1);
    }

    /* Lastly send a reset command to and turn character echo off
     * (it's not clear that turning echo off has any effect)
     */
    resetInterface();
    sendATCommand("E0");
    debug("ELM327: Connection established.", 1);
  }


Having got a connection we then need some methods to provide a simple interface for sending commands and getting back the results.  Here's the common methods for sending messages.

  /**
   * Send an AT command to control the ELM327 interface
   *
   * @param command The command string to send
   * @return The response from the ELM327
   * @throws IOException If there is a communication error
   */
  protected String sendATCommand(String command) throws IOException {
    /* Construct the full command string to send.  We must remember to
     * include a carriage return (ASCII 0x0D)
     */
    String atCommand = "AT " + command + CR_LF;
    debug("ELM327: Sending AT command [AT " + command + "]", 1);

    /* Send it to the interface */
    elmOutput.write(atCommand.getBytes());
    debug("ELM327: Command sent", 1);
    String response = getResponse();

    /* Delete the command, which may be echoed back */
    response = response.replace("AT " + command, "");
    return response;
  }

  /**
   * Send an OBD command to the car via the ELM327.
   *
   * @param command The command as a string of hexadecimal values
   * @return The number of bytes returned by the command
   * @throws IOException If there is a problem communicating
   */
  protected int sendOBDCommand(String command)
      throws IOException, ELM327Exception {
    byte[] commandBytes = byteStringToArray(command);

    /* A valid OBD command must be at least two bytes to indicate the mode
     * and then the information request
     */
    if (commandBytes.length < 2)
      throw new ELM327Exception("ELM327: OBD command must be at least 2 bytes");

    byte obdMode = commandBytes[0];

    /* Send the command to the ELM327 */
    debug("ELM327: sendOBDCommand: [" + command + "], mode = " + obdMode, 1);
    elmOutput.write((command + CR_LF).getBytes());
    debug("ELM327: Command sent", 1);

    /* Read the response */
    String response = getResponse();

    /* Remove the original command in case that gets echoed back */
    response = response.replace(command, "");
    debug("ELM327: OBD response = " + response, 1);

    /* If there is NO DATA, there is no data */
    if (response.compareTo("NO DATA") == 0)     
      return 0;

    /* Trap error message from CAN bus */
    if (response.compareTo("CAN ERROR") == 0)
      throw new ELM327Exception("ELM327: CAN ERROR detected");

    rawResponse = byteStringToArray(response);
    int responseDataLength = rawResponse.length;

    /* The first byte indicates a response for the request mode and the
     * second byte is a repeat of the PID.  We test these to ensure that
     * the response is of the correct format
     */
    if (responseDataLength < 2)
      throw new ELM327Exception("ELM327: Response was too short");

    if (rawResponse[0] != (byte)(obdMode + OBD_RESPONSE))
      throw new ELM327Exception("ELM327: Incorrect response [" +
          String.format("%02X", responseData[0]) + " != " +
          String.format("%02X", (byte)(obdMode + OBD_RESPONSE)) + "]");

    if (rawResponse[1] != commandBytes[1])
      throw new ELM327Exception("ELM327: Incorrect command response [" +
          String.format("%02X", responseData[1]) + " != " +
          String.format("%02X", commandBytes[1]));

    debug("ELM327: byte count = " + responseDataLength, 1);

    for (int i = 0; i < responseDataLength; i++)
      debug(String.format("ELM327: byte %d = %02X", i, rawResponse[i]), 1);

    responseData = Arrays.copyOfRange(rawResponse, 2, responseDataLength);

    return responseDataLength - 2;
  }

  /**
   * Send an OBD command to the car via the ELM327. Test the length of the
   * response to see if it matches an expected value
   *
   * @param command The command as a string of hexadecimal values
   * @param expectedLength The expected length of the response
   * @return The length of the response
   * @throws IOException If there is a communication error or wrong length
   */
  protected int sendOBDCommand(String command, int expectedLength)
      throws IOException, ELM327Exception {
    int responseLength = this.sendOBDCommand(command);

    if (responseLength != expectedLength)     
      throw new IOException("ELM327: sendOBDCommand: bad reply length ["
          + responseLength + " != " + expectedLength + "]");

    return responseLength;
  }


and the method for reading back the results.

  /**
   * Get the response to a command, having first cleaned it up so it only
   * contains the data we're interested in.
   *
   * @return The response data
   * @throws IOException If there is a communications problem
   */
  private String getResponse() throws IOException {
    boolean readComplete = false;
    StringBuilder responseBuilder = new StringBuilder();

    /* Read the response.  Sometimes timing issues mean we only get part of
     * the message in the first read.  To ensure we always get all the intended
     * data (and therefore do not get confused on the the next read) we keep
     * reading until we see a prompt character in the data.  That way we know
     * we have definitely got all the response.
     */
    while (!readComplete) {
      int readLength = elmInput.read(rawResponse);
      debug("ELM327: Response received, length = " + readLength, 1);

      String data = new String(Arrays.copyOfRange(rawResponse, 0, readLength));
      responseBuilder.append(data);

      /* Check for the prompt */
      if (data.contains(PROMPT)) {
        debug("ELM327: Got a prompt", 1);
        break;
      }
    }

    /* Strip out newline, carriage return and the prompt */
    String response = responseBuilder.toString();
    response = response.replace(CR, "");
    response = response.replace(LF, "");
    response = response.replace(PROMPT, "");
    return response;
  }


Using these methods it becomes pretty simple to implement methods that start to expose the OBD protocol.  For example to get the version information about the interface we just need this simple method:

  /**
   * Get the version number of the ELM327 connected
   *
   * @return The version number string
   * @throws IOException If there is a communications problem
   */
  public String getInterfaceVersionNumber() throws IOException {
    return sendATCommand("I");
  }


Another very useful method is one that returns the details about which of the PIDs are supported for a given mode.

  /**
   * Determine which PIDs for OBDII are supported. The OBD standards docs are
   * required for a fuller explanation of these.
   *
   * @param pid Determines which range of PIDs support is reported for
   * @return An array indicating which PIDs are supported
   * @throws IOException If there is a communication error
   */
  public boolean[] getPIDSupport(byte pid) throws IOException, ELM327Exception {
    int dataLength = sendOBDCommand("01 " + String.format("%02X", pid));

    /* If we get zero bytes back then we assume that there are no
     * supported PIDs for the requested range
     */
    if (dataLength == 0)
      return null;

    int pidCount = dataLength * 8;
    debug("ELM327: pid count = " + pidCount, 1);
    boolean[] pidList = new boolean[pidCount];
    int p = 0;

    /* Now decode the bit map of supported PIDs */
    for (int i = 2; i < dataLength; i++)
      for (int j = 0; j < 8; j++) {
        if ((responseData[i] & (1 << j)) != 0)
          pidList[p++] = true;
        else
          pidList[p++] = false;
      }

    return pidList;
  }


The PIDs 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0 and 0xC0 of mode 1 will report back the supported PIDs for the following 31 values as a four byte bit map.  There appear to only be definitions for commands up to 0x87 in the specification I found.

In the next part we'll look at how we can start to use this class to get some real data from the car.

Thursday Apr 25, 2013

The Raspberry Pi JavaFX In-Car System (Part 1)

Raspberry Pi JavaFX Car System (Pt 1) As part of my work on embedded Java I'm always on the look out for new ideas for demos to build that show developers how easy it is to use and how powerful.  In some of my recent web surfing I came across an interesting device on eBay that I thought had real potential.  It's called an ELM327 OBDII CAN bus diagnostic interface scanner.  It is a small box that plugs in to the service port of a modern car and provides an interface that allows software to talk to the Electronic Control Units (ECUs) fitted in your car.  The one I bought provides a Wi- Fi link and also includes a USB socket for wired connectivity.  Similar products are available that provide a BlueTooth interface, but the various opinions I read indicated that these were not as easy to use.  Considering it cost a little over £30 I thought it was well worth it for some experimentation.

Here's a picture of the device:

ELM327

And here it is plugged into the service port located near the pedals on my car. 

ELM327-in-car

The only downside is that the orientation of the socket means that you can't see the status lights when it's plugged in (at least not without a mirror).

My initial thoughts were to look at what kind of data could be extracted from the car and then write some software that would provide realtime display of things that aren't shown through the existing instrumentation.  I thought it would also be fun to record journey data that could be post-analysed in much the way Formula 1 uses masses of telemetry to let the drivers know where they could do better.

Since I wanted to use embedded Java the obvious choice of processing unit was the Raspberry Pi.  It's cheap, I have a whole bunch of them and it's got plenty of computing power for what I have in mind.  It also has some other advantages:
  • Low power consumption (easy to run off the 12V cigarette lighter supply)
  • Support for JavaFX through some nice touch screens from Chalkboard Electronics (so I can go wild with the interface)
  • Easily accessible GPIO pins
The last point got me thinking about what other possibilities there were for my in-car system.  Recently my friend and colleague Angela Caicedo did a session at Devoxx UK entitled, "Beyond Beauty: JavaFX, Parallax, Touch, Gyroscopes and Much More".  Part of this involved connecting a motion sensor to the Raspberry Pi using the I2C interface that is also available.  The particular sensor she used is from Sparkfun and uses a very cool single chip solution from InvenSense, the MPC-6150.  This provides 9-axis motion data, which means acceleration and rate of rotation for the X, Y and Z axes as well as a compass sensor that works regardless of the orientation of the sensor.

Having studied physics at university (a long time ago, in a galaxy far, far away) I vaguely remember that if I combine acceleration data with the mass of the car and things like engine speed I can calculate the horse power of the engine as well as the torque being generated.  Throw that into the mix and this could make a really fun project.

As further inspiration I came across this video recently:



There's also an interesting one from Tesla who use a 17" touch display as their cemtre console.

In the follow up parts to this blog entry I'll detail how the project evolves.



Monday Jan 21, 2013

Building an SD Card Image For a Raspberry Pi Java Hands On Lab

Building an SD Card Image For a Raspberry Pi Hands On Lab Last year we ran a very successful hands on lab for developers at Devoxx in Antwerp.  The concept was to have 40 people in a room, give them all a Raspberry Pi, cables and a pre-configured SD card and get them to build cool JavaFX apps.  One of the things I had to do was organise all the equipment and make a suitable image for the SD card.  As this was before Oracle had announced the early access of JDK8 for the Raspberry Pi with hard float support we used the soft float of Java SE embedded version 7 for ARMv6 and a non-production build of JavaFX.  As we're repeating this lab at JFokus in a couple of weeks I thought it might be useful to write up how I built the SD image as there may well be people who want to run something similar.

Hardware Setup

To simplify matters from a hardware perspective (and to make the lab economically viable) we decided not to provide attendees with monitors, keyboards and mice.  All interaction with the Pi would need to be via the network connection.  To eliminate the need for two power outlets per attendee we also decided to use USB Y power cables that can draw power from two USB ports on the attendee's laptop.  Since USB ports are rated at 500mA two would give us more than the minimum 700mA required for the Pi (as a side note I've found that you can happily boot a Pi from one USB port on a MacBook Pro - although that is without any USB peripherals attached to the Pi). 

With no monitor or USB keyboard/mouse all interaction would be via the network connection.  Again, to simplify the infrastructure we provided all attendees with an ethernet cross-over cable.  One end is connected to the ethernet port on the attendee's laptop, the other to the ethernet port on the Raspberry Pi.

The hardware setup is shown in the diagram below:
Machine setup

Software Setup

For this part I'll describe the setup necessary for the Rasbian distro so we can use the new JDK8 EA build.  One issue with this is that the JavaFX libraries included no longer supprt rendering via X11.  Since the ARM port of JavaFX is aimed at embedded devices like parking meters and point-of-sale devices we don't expect these to use an X based desktop underneath.  Now that rendering is only supported directly to the framebuffer (which gives us significantly better performance) projecting the JavaFX applications back to the attendees laptop via VNC will no longer work.  Although there is a package calld fbvnc this will not work as the rendering on the Pi does not use areas of memory that are accessible this way.

Here is a step-by-step guide:
  1. Install the Rasbian distro on an SD card.  I use 4Gb SanDisk class 4 cards which provide enough space, work with the Pi and are cheap.  When you need to replicate a significant number of cards, smaller is quicker.  To install the distro either use DiskImager (on Windows) or a simple dd command on Linux or Mac (detailed instructions can be found on the Raspberry Pi web site).
  2. Put this in a Pi and boot.  I do this with a monitor and USB keyboard connected to make life simpler.  When the Pi has finished booting you will be presented with a screen as shown:
menu
  1. Move down to expand_rootfs and select this by pressing RETURN.  This will expand the filesystem to fill the available space on the SD card.
  2. Select overclock and accept the notice about potentially reducing the lifetime of your Pi.  Remember: live fast, die young.  Seriously, though, given the cost of the Pi and the fact that the manufacturers will honour the warranty for anything up to a 1GHz clockrate and I think this is pretty safe.  I go for the medium setting of 900MHz.  This has not given me any issues, although you may want to go higher or lower as preferred.
  3. Select SSH.  I think this is enabled by default, but just to make sure select it.
  4. Lastly on this screen select update.  This will update any packages necessary in the Linux distribution.  Obviously for this you will need your Pi connected to a network where it can find the internet settings via DHCP, etc.
  5. Tab to 'Finish', hit RETURN and you will be dropped into a shell.
  6. Being an old school UNIX hacker I really don't like sudo, so the first thing I do is sudo bash and then set a password for root so I can su whenever I need to.
  7. There is a user account, pi, that is created by default.  For our labs I create a separate account for attendees to login as.  Use something like useradd -u 1024 -d /home/lab -m -s /bin/bash lab.  Remember to set the user's password: passwd lab.
  8. Since we want things to be as simple as possible we setup a DHCP server on the Pi.  Before we do that we need the Pi to use a static IP address.  Edit the /etc/network/interfaces file and change

  9. iface eth0 inet dhcp

    to

    iface eth0 inet static
        address 10.0.0.8
        gateway 10.0.0.254    

        netmask 255.255.255.0


    In these settings I've used a class A private network which is the same as the one I use in my office.  This makes things easy, as I can also configure the gateway so that the Pi can access the internet which will be required for the next stages.  If you are using a class C private network (like 192.168.0.X) you will need to change this accordingly.

    At this point I reboot the machine with the monitor and keyboard disconnected and switch to doing everything over SSH.

  10. Login over the network using SSH (use either the lab account or the pre-installed pi one) and su to root. 
  11. Install the DHCP server package, apt-get install isc-dhcp-server
  12. Configure DHCP by editing the /etc/dhcp/dhcpd.conf file.  Under the comment line 
     # This is a very basic subnet declaration.
    Add

    subnet 10.0.0.0 netmask 255.255.255.0 {
        range 10.0.0.164 10.0.0.170;
    }

    This will provide an IP address in the range from 164 to 170. Having seven available addresses is a bit of overkill, but gives us some flexibility (change your IP addresses as necessary).  In addition you must comment out these two lines at the start of the file:

    option domain-name "example.org";
    option domain-name-servers ns1.example.org, ns2.example.org;

    This was one of the things that changed between the soft float Wheezy distro and the hard float Raspbian distro.  It took me ages to figure out why the DHCP server would not work properly on Raspbian.  When I used the soft float distro all I needed to do was add the subnet and range definition.  On Raspbian the DHCP server refused to serve IP addresses even though the log messages seemed to indicate that it was fine.  After I did a diff on the dhcp.conf files from both I noticed the two lines that had been uncommented.  I commented them out again and everything worked fine.
For our lab the attendees wrote the code on their laptops using the NetBeans IDE and then transferred the project across to the Pi to run.  To make life as easy as possible the Pi is configured to support multiple ways of getting files onto it: FTP, NFS and Samba.
  1. Install the FTP server package, apt-get install proftpd-basic.  Although it would seem logical to want to run this from inetd, choose the standalone option as this actually works better and gets started, quite happily, at boot time.
  2. Configure the FTP server by editing the /etc/proftpd/proftpd.conf file.  This is not strictly necessary, but if you want to be able to use anonymous ftp then uncomment the sizeable section that starts with the comment,

    # A basic anonymous configuration, no upload directories.

  3. Install the necessary packages for NFS server support, apt-get install nfs-kernel-server nfs-common
  4. Edit the /etc/exports file to add the user home directory,

    /home/lab     10.0.0.*(rw,sync,no_subtree_check)
At this point you would think, like I did, that rebooting the machine would give you a functioning NFS server.  In fact on the soft float Wheezy distro this is exactly what happened.  As with DHCP there is some weirdness in terms of changes that were made between the soft float Wheezy distro and the Raspbian one.  With Raspbian if you use the showmount -e command, either locally or remotely you get the somewhat cryptic error message, clnt_create: RPC: Port mapper failure - RPC: Unable to receive.

I'm sure with hindsight I should have been able to solve this quicker, but having had it working fine on Wheezy I just couldn't figure out why the same thing didn't work on Raspbian.  Evantually after much Googling and head scratching I determined that it was down to the RPC bind daemon not being started at boot time.  Some kind and thoughtful person decided that RPC didn't need to run at boot time.  Rather than leaving the package out so that when it's needed it gets installed and correctly configured they just moved the links from /etc/rc2.d and /etc/rc3.d from being S (for start) to K (for kill), so it doesn't start.
  1. Make the RPC bind daemon start at boot time by running update-rc.d rpcbind enable (as root)
  2. Install the Samba packagaes with apt-get install libcups2 samba samba-common
  3. Configure samba.  Edit the /etc/samba/smb.conf file and add the following at the end of the file:

    [lab]
    comment = Raspberry Pi Java Lab
    path = /home/lab
    writable = yes
    guest ok = yes
If you want the attendees to be able to project the desktop of the Pi to their laptops then you will need VNC.
  1.  Install the VNC server, apt-get install tightvncserver
  2. As the lab user, set a password for the VNC server with tightvncpasswd.  When doing this you can set different passwords for a fully interactive session and a view only one.
  3. Run tightvncserver :1 to generate all the necessary configuration files.  You will now be able to access the Raspberry Pi desktop remotely using a VNC client (I use [the bizarrely named] Chicken of the VNC on the Mac, RealVNC on Windows and xtightvncviewer on Linux).
  4. In order for the VNC server to start up whenever the system boots a script is required in the /etc/init.d directory.  I call it tightvncviewer, for which the code is:

    #!/bin/sh -e
    #
    # Start/stop VNC server

    ### BEGIN INIT INFO
    # Provides:          tightvncserver
    # Required-Start:    $network $local_fs
    # Required-Stop:     $network $local_fs
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: tightvncserver remote X session projection
    # Description:       tightvncserver allows VNC clients to connect to
    #                    this machine and project the X desktop to the
    #                    remote machine.
    ### END INIT INFO

    . /lib/lsb/init-functions

    # Carry out specific functions when asked to by the system
    case "$1" in
      start)
        echo Starting tightVNC server
        su lab -c 'tightvncserver :1 > /tmp/vnclog 2>&1'
        ;;
      stop)
        echo Stopping tightVNC server
        su lab -c 'tightvncserver -kill :1'
        ;;
      restart)
        echo Restarting vncserver
        $0 stop
        $0 start
        ;;
      *)
        echo "Usage: /etc/init.d/vncserver {start|stop|restart}"
        exit 1
        ;;
    esac

    exit 0

    Make sure that this script has execute permission.  To create the necessary links into the /etc/rc*.d directories run update-rc.d tightvncserver defaults.  Note that this provides the desktop of the 'lab' user.  If you want to support a different user change the name.  More users can be supported by creating additional servers running on screens other than :1.
To avoid having to provide printed instructions for the lab or distribute files on a CD or memory stick I also configure Apache on the Pi so that once the Pi is connected to the attendee's laptop they can simply open a web page and have whatever instructions and software available from there.
  1. Install Apache, apt-get install apache2
  2. Create your HTML content and put it in /var/www
  3. Finally install the Java runtime.  I put it in /opt and set the PATH environment variable in the user's .bashrc file.

Tuesday Oct 16, 2012

Mind Reading with the Raspberry Pi

Mind Reading With The Raspberry Pi At JavaOne in San Francisco I did a session entitled "Do You Like Coffee with Your Dessert? Java and the Raspberry Pi".  As part of this I showed some demonstrations of things I'd done using Java on the Raspberry Pi.  This is the first part of a series of blog entries that will cover all the different aspects of these demonstrations.

A while ago I had bought a MindWave headset from Neurosky.  I was particularly interested to see how this worked as I had had the opportunity to visit Neurosky several years ago when they were still developing this technology.  At that time the 'headset' consisted of a headband (very much in the Bjorn Borg style) with a sensor attached and some wiring that clearly wasn't quite production ready.  The commercial version is very simple and easy to use: there are two sensors, one which rests on the skin of your forehead, the other is a small clip that attaches to your earlobe.

Neurosky product image 1 Neurosky product image 2

Typical EEG sensors used in hospitals require lots of sensors and they all need copious amounts of conductive gel to ensure the electrical signals are picked up.  Part of Neurosky's innovation is the development of this simple dry-sensor technology.  Having put on the sensor and turned it on (it powers off a single AAA size battery) it collects data and transmits it to a USB dongle plugged into a PC, or in my case a Raspberry Pi.

From a hacking perspective the USB dongle is ideal because it does not require any special drivers for any complex, low level USB communication.  Instead it appears as a simple serial device, which on the Raspberry Pi is accessed as /dev/ttyUSB0.  Neurosky have published details of the command protocol.  In addition, the MindSet protocol document, including sample code for parsing the data from the headset, can be found here.

To get everything working on the Raspberry Pi using Java the first thing was to get serial communications going.  Back in the dim distant past there was the Java Comm API.  Sadly this has grown a bit dusty over the years, but there is a more modern open source project that provides compatible and enhanced functionality, RXTXComm.  This can be installed easily on the Pi using sudo apt-get install librxtx-java

Next I wrote a library that would send commands to the MindWave headset via the serial port dongle and read back data being sent from the headset.  The design is pretty simple, I used an event based system so that code using the library could register listeners for different types of events from the headset.  You can download a complete NetBeans project for this here.  This includes javadoc API documentation that should make it obvious how to use it (incidentally, this will work on platforms other than Linux.  I've tested it on Windows without any issues, just by changing the device name to something like COM4).

To test this I wrote a simple application that would connect to the headset and then print the attention and meditation values as they were received from the headset.  Again, you can download the NetBeans project for that here.

Oracle recently released a developer preview of JavaFX on ARM which will run on the Raspberry Pi.  I thought it would be cool to write a graphical front end for the MindWave data that could take advantage of the built in charts of JavaFX.  Yet another NetBeans project is available here.  Screen shots of the app, which uses a very nice dial from the JFxtras project, are shown below.

JavaFX Mind Reader

JavaFX Mind Reader

I probably should add labels for the EEG data so the user knows which is the low alpha, mid gamma waves and so on.  Given that I'm not a neurologist I suspect that it won't increase my understanding of what the (rather random looking) traces mean.

In the next blog I'll explain how I connected a LEGO motor to the GPIO pins on the Raspberry Pi and then used my mind to control the motor!

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.

Monday Apr 30, 2012

JavaFX on the Raspberry Pi

Over the last few days I've been playing with the Raspberry Pi board (I was lucky enough to secure one of these as part of the work Oracle is doing to ensure that Java runs smoothly on it). 

Initial setup was pretty straightforward although I did need to check the current rating of the power supply I was using as the board needs 700mA, which is more than a normal USB port or hub supplies.  I used Win32DiskManager to copy the OS image to an SD card and then gparted to resize the partition to use the whole 8Gb on the card rather than just 2Gb.  Since I don't have a spare monitor I decided to do most things remotely over the network and set up sshd without any problem.

There is an OpenJDK build for ARM, but it doesn't have JIT support, so performance is not optimal.  Oracle provides a commercial implementation, which does have JIT support, so I downloaded and installed this which was painless. (The only thing to note here is I used the vfp version of the JDK).

Next, I figured I'd have a go at building JavaFX from source.  I grabbed a snapshot and set about seeing what needed to be done.  The first problem was that the reported platform didn't match anything in the build description (ARM v. x86).  Being a bit lazy I cheated.  Rather than configuring a whole new set of build rules I just overrode the architecture setting and forced it to i586.  Since the compiler is ARM, what's the worst that could happen?  This got me started, but then I bumped in to issues with some of the native media code compiling with SSE2 optimisations that don't exist on ARM.  A short learning curve later I switched to the ARM NEON equivalent, changed some definitions and got a bit further.  There were a few more issues around missing packages and left over .o files and then a stroke of real luck. 

Someone in Oracle contacted me to say that we already had an internal build of JavaFX for ARM which had been done for the Beagle Board and pointed me to where I could download it, which I duly did.  I copied over all the necessary bits as well as one of my own, simple JavaFX apps compiled into a jar.  After a few command line mis-starts I had JavaFX up and running on the Raspberry Pi!

Here's a quick video that shows the results:

At the moment this is as far as I've got.  I did try a more complex application, but ran into a problem with a missing libmat.so library.  Something to track down later.

Getting the remote desktop working was a bit of a challenge, but not because of anything to do with the Raspberry Pi or Java.  Initially I thought I could use ssh -X and then project the X application (JavaFX) back to my Mac so I could do the screen capture.  Although this worked fine if the client machine was Linux every time I tried it on Mac OS X I got an access error for MIT-SHM.  It seemed the X environment was trying to use shared memory, which when the client and server are on different machines is not going to work.  I tried several ways to try and convince the system not to use the shared memory extensions, but it just wasn't happening.  In the end I gave up and decided that the path of least resistance was to use VNC and have the whole desktop visible on the Mac.

Stay tuned for more updates.

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