Beyond Beauty: JavaFX, I2C, Parallax, Touch, Raspberry Pi, Gyroscopes and Much More. (Part I)
By Angela Caicedo-Oracle on May 03, 2013
When I
think about realism on user interfaces, the first thing that
comes to my mind is 3D. Unfortunately, 3D and
embedded are two worlds that don't go along very well.
When we think about embedded, we think about constrained
devices, but it doesn't have to mean ugly interfaces. Now
we have Java SE Embedded (which includes JavaFX), it provides us
with a great tool to create astonishing UI for embedded
applications.
This
was my inspiration for my next
demo, what can I build, that could be cool, applied to
embedded, and of course, how can I play with new hardware that I
haven't played with it yet! :-) That was my new and
exciting challenge.
The inspiration
The hardware:
- Raspberry Pi was definitely the device, I could easily have Java and JavaFX on it.
- I want to build an interface that reacts to the device orientation. For the purpose of tracking positioning I used a combination of accelerometer + gyroscope, and the MPU-9150 Breakout from sparkfun electronics, was just perfect for this. The MPU-9150 is a System in Package (SiP) that combines two chips: the MPU-6050, which contains a 3-axis gyroscope, 3-axis accelerometer, and an onboard Digital Motion Processor (DMP) capable of processing complex 9-axis MotionFusion algorithms; and the AK8975, a 3-axis digital compass. The supply voltage range was from 2.4V to 3.46V, just perfect for our RPi, and it uses I2C as a communication protocol also supported by RPi.

- For screen, I choose the 10" LDC touchscreen from Chalkboard Electronics. It connect with HDMI/USB so it will also work with my RPi. All the instructions about how to have this screen working with your Pi can be found here.

The software
- I'm using Oracle
Java SE Embedded and JavaFX apis for the user
interface.
-
I'm using I2C for the communication protocol. Fortunately, I found Pi4J apis that just do the job for me. It has a great set of APIs to hide all I2C low level details, by exposing a really clean set of APIs, very easy to use.
- The parallax
effect. Some time ago I saw a very cool applications
using the Parallax effect, it provides a lot of realism to the
scene without adding the extra processing cost of the 3D
rendering. Parallax effect is a scrolling technique in
computer graphics, wherein background images move by the
camera slower than foreground images, creating an illusion of
depth in a 2D interface and adding realism to the
immersion. We see it everyday, just pay attention on how
things move when you drive around.

Getting I2C working on the Raspberry Pi
If you pay attention to the Raspberry Pi pins you will see that it has two pins for I2C, one for data's bus (SDA) and one for the clock (SCL).

Great!, I was ready to
start connecting things... But to my surprise, I2C is not
enabled by default on the RPi, and there are few steps you need
to follow.
Enabling I2C in the RPi
- First go to: /etc/modules and add the following lines:
- i2c-bcm2708
- i2c-dev
- Install i2c-tools. This is not required, but it's very handy for detecting devices and making sure everything works properly.
- sudo apt-get install python-smbus
- sudo apt-get install i2c-tools
- There is a file
called raspi-blacklist.conf, and by default SPI and I2C
are part of this black list!
- Edit /etc/modprobe.d/raspi-blacklist.conf
and comment out the
lines
- blacklist spi-bcm2708
- blacklist i2c-bcm2708
Connecting the
Gyroscope/Accelerometer to the Pi.
Connecting the board
and the RPi is pretty straight forward. I2C requires
pullup resistors, but the RPi provide them, so we just
need to connect the MPU-9150 directly to the Pi, following the
next diagram:

The MPU-9150 has a AD0
pin, this allows you to set the last bit of the board's
address, to either 0 (ground) or 1 (Vcc). This
allows us to connect two boards to the same bus. In my
case I just grounded this pin, then my board address is 0x68
Verifying the communication:
Once we connect the
board to the RPi, we can continue to check if we can actually
see it as a I2C device.
- Try running on your
pi: sudo i2cdetect -y 1 or
sudo
i2cdetect -y 0. (0 for the 256 Pi model B).
- You should be able
to see your device on the table. The following snapshot
shows two I2C devices, one at address 40 and the second on
address 70.

- You should also see a couple of new entries under /dev:
- spidev0.0
- spidev0.1
- I2c-0
- I2c-1
- If don't see any of these entries, try running:
- sudo
modprobe i2c-dev
The Hardware setup
This is the big picture
of the demo setup. I just mounted everything on a piece of
wood, then, changes on the board position and direction will be
reflected on the UI rendered on the screen.

The software
WiringPi Native library.
WiringPi is an
Arduino wiring-like library written in C and released under the
GNU_LGPLv3 license, which is usable from C and C++ and many
other languages with suitable wrappers. This library
provides access to GPIOs pin, I2C and SPI interface, and UART
lines.
WiringPi
includes a command-line utility gpio
which can be used to program and setup the GPIO pins. You can
use this to read and write the pins and even use it to control
them from shell scripts.
Pi4J
Provides a bridge
between the native libraries (WiringPi) and Java for full
access to the Raspberry Pi.
- Wrapper classes for direct access to WiringPi Library from Java
- Export & unexport GPIO pins
- Configure GPIO pin direction
- Configure GPIO pin edge detection
- Control/write GPIO pin states
- Pulse GPIO pin state
- Read GPIO pin states
- Listen for GPIO pin state changes (interrupt-based; not polling)
- Automatically set GPIO states on program termination (GPIO shutdown)
- Triggers for automation based on pin state changes
- Send & receive
data via RS232 serial communication
- I2C Communication
- SPI Communication
- Extensible GPIO
Provider interface to add GPIO capacity via expansion boards
- Access system
information and network information from the Raspberry Pi
- Wrapper classes for
direct access to WiringPi Library from Java
Installing and using Pi4J
- First, get the APIs.
From the Pi command line type:
- wget http://pi4j.googlecode.com/files/pi4j-0.0.5.deb
- Install the packages
- sudo dpkg -i pi4j-0.0.5.deb
- Check the source
files at:
- /opt/pi4j/lib
- /opt/pi4j/examples
- Make sure you
include the Pi4J lib folder in the classpath:
- javac -classpath .:classes:/opt/pi4j/lib/'*' ...
- sudo java -classpath
.:classes:/opt/pi4j/lib/'*' ...
- Pi4J comes with a
set of demos, you can build them and start from there.
- Build all demos:
- cd /opt/pi4j/examples
- ./build
- At any time you can uninstall Pi4J
- sudo dpkg -r pi4j
The Code:
First, we need to get the I2CBus. I2CFactory class has a method called getInstance that allows us to do that. Once we get the bus, we get the device itself (our board), just make sure you provide the correct address (did you ground or not the AD0 pin on your board?), in our case we grounded AD0 and our address is x68).
public class Sensors { I2CBus bus; I2CDevice device; public Sensors() { System.out.println("Starting sensors reading:"); // get I2C bus instance try { //get i2c bus bus = I2CFactory.getInstance(I2CBus.BUS_1); System.out.println("Connected to bus OK!"); //get device itself device = bus.getDevice(0x68); System.out.println("Connected to device OK!"); ... } ... } |
After getting the
device, I thought I was going to get the readings right away,
but it wasn't the case. For the MPU-9150 few registries
need to be set before the reading start: (Find full
description at the MPU_9150
Register
Map and Description Revision)
- Register 107 – Power Management 1 PWR_MGMT_1
This register allows the user to configure the power mode and clock source. It also provides a bit for resetting the entire device, and a bit for disabling the temperature sensor.
Parameters:
DEVICE_RESET When set to 1, this bit resets all internal registers to their default values. SLEEP When set to 1, this bit puts the MPU-9150 into sleep mode. CYCLE When this bit is set to 1 and SLEEP is disabled, the MPU-9150 will cycle between sleep mode and waking up to take a single sample of data from active sensors at a rate determined by LP_WAKE_CTRL (register 108). TEMP_DIS When set to 1, this bit disables the temperature sensor. CLKSEL 3-bit unsigned value. Specifies the clock source of the device.
-
Register 108 – Power Management 2 PWR_MGMT_2

This register allows the user to configure the frequency of wake-ups in Accelerometer Only Low Power Mode. This register also allows the user to put individual axes of the accelerometer and gyroscope into standby mode.
Parameters:
LP_WAKE_CTRL 2-bit unsigned value. Specifies the frequency of wake-ups during Accelerometer Only Low Power Mode. STBY_XA When set to 1, this bit puts the X axis accelerometer into standby mode. STBY_YA When set to 1, this bit puts the Y axis accelerometer into standby mode. STBY_ZA When set to 1, this bit puts the Z axis accelerometer into standby mode. STBY_XG When set to 1, this bit puts the X axis gyroscope into standby mode. STBY_YG When set to 1, this bit puts the Y axis gyroscope into standby mode. STBY_ZG When set to 1, this bit puts the Z axis gyroscope into standby mode.
-
Register 27: Gyroscope Configuration GYRO_CONFIG
This register is used to trigger gyroscope self-test and configure the gyroscopes’ full scale range.
The self-test for each gyroscope axis can be activated by controlling the XG_ST, YG_ST, and ZG_ST bits of this register.
FS_SEL is a 2-bit unsigned value and it allows us to select the full scale range of gyroscopes.
-
Register 28: Accelerometer Configuration ACCEL_CONFIG
This register is used to trigger accelerometer self test and configure the accelerometer full scale range.
The self-test for each accelerometer axis can be activated by controlling the XA_ST, YA_ST, and ZA_ST bits of this register.
Parameters:
XA_ST When set to 1, the X - Axis accelerometer performs self - test YA_ST When set to 1, the Y - Axis accelerometer performs self - test ZA_ST When set to 1, the Z - Axis accelerometer performs self - test ACCEL _FS_SE 2-bit unsigned value . Selects the full scale range of accelerometers. ACCEL_HPF 3-bit unsigned value. Selects the Digital High Pass Filter configuration.
Now setting these registries looks like this:
public class Sensors { I2CBus bus; I2CDevice device; public Sensors() { System.out.println("Starting sensors reading:"); // get I2C bus instance try { //get i2c bus bus = I2CFactory.getInstance(I2CBus.BUS_1); System.out.println("Connected to bus OK!"); //get device itself device = bus.getDevice(0x68); System.out.println("Connected to device OK!"); //start sensing, using config registries 6B and 6C device.write(0x6B, (byte) 0b00000000); device.write(0x6C, (byte) 0b00000000); System.out.println("Configuring Device OK!"); //config gyro device.write(0x1B, (byte) 0b11100000); //config accel device.write(0x1C, (byte) 0b00011001); System.out.println("Configuring sensors OK!"); startReading(); } catch (IOException e) { System.out.println(e.getMessage()); } } ... } |
Now how to read the gyroscope and accelerometers values? The values for the accelerometer measurements are stored in registers 59 to 64. Each measurement is a 16-bit value, stored in two consecutive registers, as shown in the following table.

Similarly, the gyroscope measurements are stored in registers 67-72.

Now the code will look like this:
public class Sensors { I2CBus bus; I2CDevice device; byte[] accelData, gyroData; public Sensors() { System.out.println("Starting sensors reading:"); // get I2C bus instance try { //get i2c bus bus = I2CFactory.getInstance(I2CBus.BUS_1); System.out.println("Connected to bus OK!"); //get device itself device = bus.getDevice(0x68); System.out.println("Connected to device OK!"); //start sensing, using config registries 6B and 6C device.write(0x6B, (byte) 0b00000000); device.write(0x6C, (byte) 0b00000000); System.out.println("Configuring Device OK!"); //config gyro device.write(0x1B, (byte) 0b11100000); //config accel device.write(0x1C, (byte) 0b00011001); System.out.println("Configuring sensors OK!"); startReading(); } catch (IOException e) { System.out.println(e.getMessage()); } } //Create a separate thread for reading the sensors public void startReading() { Task task = new Task<Void>() { @Override public void call() { try { readingSensors(); } catch (IOException e) { } return null; } }; new Thread(task).start(); } private void readingSensors() throws IOException { while (true) { accelData = new byte[6]; gyroData = new byte[6]; //You can read one registry at a time, //or you can read multiple consecutive ones, //in our case we are reading 6 consecutive registries //from 0x3B, meaning we are reading all the //accelerometer measurements int r = device.read(0x3B, accelData, 0, 6); if (r != 6) { System.out.println("Error reading accel data, < 6 bytes"); } //Convert the values to integers, using the //helper method asInt int accelX = asInt(accelData[0]) * 256 + asInt(accelData[1]); int accelY = asInt(accelData[2]) * 256 + asInt(accelData[3]); int accelZ = asInt(accelData[4]) * 256 + asInt(accelData[5]); System.out.println("accelX: " + accelX + ", accelY: " + accelY + ", accelZ: " + accelZ); //Reading gyroscope measurements. r = device.read(0x43, gyroData, 0, 6); if (r != 6) { System.out.println("Error reading gyro data, < 6 bytes"); } //Convert the values to integers, using the //helper method asInt gyroX = (asInt(gyroData[0]) * 256 + asInt(gyroData[1])); gyroY = (asInt(gyroData[2]) * 256 + asInt(gyroData[3])); gyroZ = (asInt(gyroData[4]) * 256 + asInt(gyroData[5]); System.out.println("gyroX: " + gyroX + ", gyroY: " + gyroY + ", gyroZ: " + gyroZ); //Use the values as you want ... } } //Helper method private static int asInt(byte b) { int i = b; if (i < 0) { i = i + 256; } return i; } } |
After you get the values from the accelerometer and gyroscope, you can use them as you wish. In my next blog entry we see how this values are going to be used to update the UI to provide realism to our interface.
Hope you enjoyed! See video of the demo here

