by Jose Cruz
Learn how to connect sensors to the Raspberry Pi and control them with Java.
Part 1 of this series explained how to connect electronic sensors to the Raspberry Pi Model B using a general-purpose input/output (GPIO) interface. Part 2 focused on using inter-integrated circuit bus (I2C) interfaces to connect sensors. Part 3 showed how to use universal asynchronous receiver transmitter circuit (UART) interfaces to connect a GPS receiver board and a text-to-speech module, as well as how to convert an I2C interface to UART.
This article, which is the last in this series, focuses on using a serial peripheral interface (SPI) bus to connect the following modules:
Note: The complete example code for this NetBeans IDE 8.0.2 project can be downloaded from the attachment below.
Enabling SPI on the Raspberry Pi
SPI is a synchronous serial communication interface specification used for short-distance communication, mainly in embedded systems.
SPI devices communicate in full-duplex mode using a master-slave architecture that has a single master device. The master device originates the frame for reading and writing using two logical signals: Master Input, Slave Output (MISO) and Master Output, Slave Input (MOSI). The master device needs a clock to synchronize with slaves. To do that, it uses the Serial Clock (SCLK) signal.
Multiple slave devices are supported by selecting individual slave select (SS) lines. The Raspberry Pi uses two logical signals, CE0 and CE1, for this purpose.
The Raspberry Pi's SPI logical signals MOSI, MISO, SCLK, CE0, and CE1 can be found at pins 19, 21, 23, 24, and 26 respectively. See the Raspberry Pi pinout.
To enable SPI on the Raspberry Pi, we need to start by removing any blacklist for the SPI module. First, run the commands shown in Listing 1 to edit the raspi-blacklist.conf
file.
$ sudo nano /etc/modprobe.d/raspi-blacklist.conf
Listing 1. Editing the raspi-blacklist.conf
file
In the file, look for the line blacklist spi-bcm2708
and insert a #
at the beginning of that line to comment it. Then press Control-X Y and press Return to save the file.
Reboot the Raspberry PI using the command shown in Listing 2.
$ sudo reboot
Listing 2. Rebooting the Raspberry Pi
To check whether the SPI module was correctly installed, use the commands shown in Listing 3:
$ ls /dev/spi*/dev/spidev0.0 /dev/spidev0.1
$ lsmod...
...
spi_bcm2708
Listing 3. Verifying that the SPI module was correctly installed
If the SPI module was not correctly installed, try to load the SPI module manually by running the command shown in Listing 4.
$ sudo modprobe spi-bcm2708
Listing 4. Loading the SPI module
A loopback test can be used to test the SPI send and receive capability. Connect a wire between the MOSI pin (19) and the MISO pin (21) and execute the commands shown in Listing 5.
$ wget https://raw.githubusercontent.com/raspberrypi/linux/rpi-3.10.y/Documentation/spi/spidev_test.c$ gcc -o spidev_test spidev_test.c$ ./spidev_test -D /dev/spidev0.0spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D
Listing 5. Loopback SPI test
If the output looks like the output shown in Listing 5, the SPI send and receive capability is working.
Circuits We Will Create
Based on the block diagram shown in Figure 1 and the components shown Figure 2, we create the circuits shown in Figure 3.

Figure 1. Block diagram of the circuits we will create

Figure 2. Components we will use

Figure 3. Schematic of the circuits we will create
Summary of the Code We Will Write
This section provides an overview of the code we will write to control the devices and to read data from them and write data to them.
For each device, we will create a device class to implement all device control operations. This class needs to extend the SPIRpi
class, which defines a device at a specified address and stores the device's SPI address in a public device
variable, as shown in Listing 6.
public class SPIRpi {
private SPIDeviceConfig config;
public SPIDevice device = null; // Save device address and info
public static final int CE0=0; // SPI address 0 CE0
public static final int CE1=1; // SPI address 1 CE1
Listing 6. Definition of the SPIRpi
class
We will also create a class constructor that initializes and activates communication for a specific SPI address using the DeviceManager
API and the SPIDeviceConfig
class to establish the following conditions (see Listing 7):
- Controller number: The number of the bus the slave device is connected to (the Raspberry Pi uses 0 and only one controller)
- SPI address: The address of the slave device on the bus
- SPI device configuration: Determines how the master activates the chip select signal to control a slave device; by default, all are low
- Clock frequency that the master synchronizes with the slave; by default, it is 2 MHz
- Mode number, which is based on the clock polarity and phase; by default, it is 0
- Address size: The address size in bits, which is 8 by default
- Bit communication order, which is big endian by default
public SPIRpi(int address) throws IOException {
config = new SPIDeviceConfig(0,
address,
SPIDeviceConfig.CS_ACTIVE_LOW,
2000000,
0,
8,
Device.BIG_ENDIAN);
device = DeviceManager.open(config);
}
Listing 7. Establishing the initial conditions for devices
It's also important to close a device to free device resources, as shown in Listing 8.
public void close() {
...
device.close();
...
}
Listing 8. Closing the device resources
This is all we will need to do to create a device class.
Connecting the MCP3008 8-Channel 10-Bit ADC
The MCP3008 is a solution for the absence of Raspberry Pi analog inputs. It adds eight channels of 10-bit analog input to projects. It has the following features:
- 10-bit resolution
- Eight input channels
- Analog inputs programmable as single-ended or pseudo-differential pairs
- On-chip sample and hold
- SPI serial interface (mode 0; mode 0 and 1; and mode 1)
- Single supply operation: 2.7 V–5.5 V
- 200 kilo samples per second (ksps) maximum sampling rate at VDD = 5 V
- 75 ksps maximum sampling rate at VDD = 2.7 V
- Low-power CMOS technology
- 5 nA typical standby current, 2 µA maximum current
- 500 µA maximum active current at 5 V
Let's now connect the device to the Raspberry Pi's GND, MOSI, MISO, SCLK, and VCC 3.3 V pins, and an additional 10K ohm potentiometer to test analog values at MCP3008 pin 1, as shown in Figure 3. Then create a Java ME 8 class called MCP3008Device
with a constructor to establish the device's SPI address to control the ADC, as shown in Listing 9.
public class MCP3008Device extends SPIRpi {
public MCP3008Device(int address) throws IOException {
super(address);
}
Listing 9. Creating the MCP3008Device
class
The readChannel
method (see Listing 10) reads an analog value from a particular channel. To do that, we need send byte 0x01
to read the channel command, we send (8 + channel number) <<4
to select the channel number, and we send byte 0x00
to start reading the MCP3008.
The Raspberry Pi (the master) receives data from the device in a three-byte array. The second and third bytes have the analog values, and with the formula ((rcvBuf.get(1) & 3) << 8) + rcvBuf.get(2)
we read all the analog data.
If we need to convert analog value to volts, we use the method convertVolts
. The MCP3008 Vref pin (15) is 3.3V, so the formula that we will use is (data * 3.3 ) / 1023
, as shown in Listing 10.
public int readChannel(int channel) throws IOException {
ByteBuffer sndBuf = ByteBuffer.wrap(new byte[]{
MCP3008.READ_CHANNEL.cmd, (byte) ((8 + channel) << 4), 0});
ByteBuffer rcvBuf = ByteBuffer.wrap(new byte[3]);
device.begin();
device.writeAndRead(sndBuf, rcvBuf);
device.end();
return ((rcvBuf.get(1) & 3) << 8) + rcvBuf.get(2);
}
public float convertVolts(int data) {
return (float) (data * 3.3) / (float) 1023;
}
}
Listing 10. Method to read a channel and convert its value to volts
Let's now create a test MIDlet class called TestMCP3008
to read analog values from the potentiometer and convert them to volts, as shown in Listing 11. Modifying the value of the potentiometer changes the analog value at channel 0.
public class TestMCP3008 extends MIDlet {
...
@Override
public void startApp() {
...
MCP3008Device mcp3008 = new MCP3008Device(SPIRpi.CE0);
int digitalData = mcp3008.readChannel(0);
Logger.getGlobal().log(Level.INFO, "Channel 0 Value:" + digitalData);
Logger.getGlobal().log(Level.INFO, "Volts at channel 0:" +
String.format("%.2f", mcp3008.convertVolts(digitalData)));
mcp3008.close();
...
}
@Override
public void destroyApp(boolean unconditional) {
}
}
Listing 11. Test MIDlet for the MCP3008 ADC
Connecting the FRAM Breakout Board
This is a breakout board for the MB85RS64V, a FRAM chip in a configuration of 8,192 words x 8 bits, using the ferroelectric process and silicon gate CMOS process technologies for forming the nonvolatile memory cells. These are some of its general features:
- Bit configuration: 8,192 words x 8 bits
- Serial Peripheral Interface (SPI)
- Correspondent to SPI mode 0 (0, 0) and mode 3 (1, 1)
- Operating frequency: 20 MHz (maximum)
- High endurance: 1012 times/byte
- Data retention: 10 years (+ 85 °C), 95 years (+ 55 °C), over 200 years (+ 35 °C)
- Operating power supply voltage: 3.0 V to 5.5 V
This chip has the following commands specified in op-codes, (op-codes are shown in hexadecimal):
- 06h: WREN (set write enable latch)
- 04h: WRDI (reset write enable latch)
- 05h: RDSR (read status register)
- 01h: WRSR (write status register)
- 03h: READ (read memory code)
- 02h: WRITE (write memory code)
- 9Fh: RDID (read device ID)
To support these registers, create an enumeration called FRAM
, as shown in Listing 12.
public enum FRAM {
OPCODE_WREN(0b0110),
OPCODE_WRDI(0b0100),
OPCODE_RDSR(0b0101),
OPCODE_WRSR(0b0001),
OPCODE_READ(0b0011),
OPCODE_WRITE(0b0010),
OPCODE_RDID(0b10011111);
public byte cmd;
private FRAM(int cmd) {
this.cmd = (byte) cmd;
}
}
Listing 12. Enumeration that defines all FRAM command op-codes
Let's now connect the FRAM breakout board to the Raspberry Pi's 3.3 V, GND, SCLK, MISO, MOSI, and CE1 pins, as shown in Figure 3, and create a Java ME 8 class called FRAMDevice
with its constructor using the control address to initialize the FRAM, as shown in Listing 13.
public class FRAMDevice extends SPIRpi {
private int manufacturerID; //Code of manufacturer
private int productID; //Product ID
public FRAMDevice(int address) throws IOException {
super(address);
}
Listing 13. FRAMDevice
class with its constructor establishing the control address
Then create the following operation methods (see Listing 14):
getManufacturerID
: Obtains the manufacturer ID code (0x04)
getProductID
: Obtains the product ID code (0x0302)
getDeviceID
: Reads the product and manufacturer ID if communication with the device is OK
write8
: Writes a byte to a memory address
writeEnabled
: Enables or disables write operations
read8
: Reads a byte from the memory address
public int getManufacturerID() {
return manufacturerID;
}
public int getProductID() {
return productID;
}
public void getDeviceID() throws IOException {
ByteBuffer sndBuf = ByteBuffer.wrap(new byte[]{FRAM.OPCODE_RDID.cmd, 0, 0, 0, 0});
ByteBuffer rcvBuf = ByteBuffer.wrap(new byte[5]);
device.begin();
device.writeAndRead(sndBuf, rcvBuf);
device.end();
manufacturerID = (rcvBuf.get(1));
productID = (rcvBuf.get(3) << 8) + rcvBuf.get(4);
}
public void write8(int addr, byte value) throws IOException {
device.begin();
device.write(FRAM.OPCODE_WRITE.cmd);
device.write((addr >> 8));
device.write((addr & 0xFF));
device.write(value);
device.end();
}
public void writeEnable(boolean enable) throws IOException {
device.begin();
if (enable) {
device.write(FRAM.OPCODE_WREN.cmd);
} else {
device.write(FRAM.OPCODE_WRDI.cmd);
}
device.end();
}
public int read8(int addr) throws IOException {
ByteBuffer sndBuf = ByteBuffer.wrap(new byte[]{
FRAM.OPCODE_READ.cmd,
(byte) (addr >> 8),
(byte) (addr & 0xFF), 0});
ByteBuffer rcvBuf = ByteBuffer.wrap(new byte[4]);
device.begin();
device.writeAndRead(sndBuf, rcvBuf);
device.end();
return rcvBuf.get(3);
}
}
Listing 14. Operation methods
Let's now create a test MIDlet class TestFRAM
to read the manufacturer and product IDs, and to write and read bytes at address 45 and address 47, as shown in Listing 15.
public class TestFRAM extends MIDlet {
...
@Override
public void startApp() {
...
FRAMDevice fram = new FRAMDevice(1);
fram.getDeviceID();
Logger.getGlobal().log(Level.INFO, "Manufacturer ID:" +
fram.getManufacturerID());
Logger.getGlobal().log(Level.INFO, "Product ID:" + fram.getProductID());
fram.writeEnable(true);
fram.write8(1, (byte) 45);
fram.writeEnable(false);
fram.writeEnable(true);
fram.write8(2, (byte) 47);
fram.writeEnable(false);
Logger.getGlobal().log(Level.INFO, "Byte at memory address 1 = " +
fram.read8(1));
Logger.getGlobal().log(Level.INFO, "Byte at memory address 2 = " +
fram.read8(2));
fram.close();
...
}
@Override
public void destroyApp(boolean unconditional) {
}
}
Listing 15. Test MIDlet for FRAM breakout board
Connecting the OLED Display
The OLED 0.96-inch SPI serial 128X64 display from SainSmart uses driver chip SSD1306. These are some of its general features:
- Power supply: 3.3 V to 5 V
- Power consumption: maximum 20 mA when all dots light are on
- Communication mode: SPI
- Works with all microcontrollers and microprocessors
- Communication signal can work on 3.3 V and 5.0 V
- Pixels: 128x64, high bright self-light emitting
We can use this display to shows numbers, words, and graphics. To do that, we port the Adafruit Arduino C Library and Adafruit Gfx Library to Java.
The SSD1306 driver chip has the following registers (addresses are shown in hexadecimal):
- 00h: Command mode
- 40h: Data mode
- A0h: Set segment remap
- A7h: Inverse display
- A8h: Set multiplex ratio
- Aeh: Display off
- Afh: Display on
- 81h: Set contrast level
- 01h: External vcc
- 02h: Internal vcc
- 21h: Set column address
- 22h: Set page address
- 2fh: Activate scroll
- 2eh: Deactivate scroll
- 26h: Right horizontal scroll
- 27h: Left horizontal scroll
- A4h: Entire display resume
- A5h: Entire display on
- A6h: Normal display
- D3h: Set display offset
- Dah: Set com pins
- Dbh: Set vcomh deselect level
- D5h: Set display clock div
- D9h: Set precharge period
- 00h: Set lower column start address
- 10h: Set higher column start address
- 40h: Set start line
- 20h: Set memory mode
- C0h: Set com output scan direction normal
- C8h: Set com output scan direction remap
- 8Dh: Charge_Pump_Setting
- A3h: Set vertical scroll area
- 29h: Vertical and right horizontal scroll
- 2Ah: Vertical and left horizontal scroll
To support these registers, create two enumerations called OLED
and SSD1306
, as shown in Listing 16 and Listing 17.
public enum OLED {
/*=========================================================================
SSDxxxx Common Displays
-----------------------------------------------------------------------
Common values to all displays
=========================================================================*/
SSD_Command_Mode(0x00), /* C0 and DC bit are 0 */
SSD_Data_Mode(0x40), /* C0 bit is 0 and DC bit is 1 */
SSD_Set_Segment_Remap(0xA0),
SSD_Inverse_Display(0xA7),
SSD_Set_Muliplex_Ratio(0xA8),
SSD_Display_Off(0xAE),
SSD_Display_On(0xAF),
SSD_Set_ContrastLevel(0x81),
SSD_External_Vcc(0x01),
SSD_Internal_Vcc(0x02),
SSD_Set_Column_Address(0x21),
SSD_Set_Page_Address(0x22),
SSD_Activate_Scroll(0x2F),
SSD_Deactivate_Scroll(0x2E),
SSD_Right_Horizontal_Scroll(0x26),
SSD_Left_Horizontal_Scroll(0x27),
Scroll_Left(0x00),
Scroll_Right(0x01),
Scroll_2Frames(0x07),
Scroll_3Frames(0x04),
Scroll_4Frames(0x05),
Scroll_5Frames(0x00),
Scroll_25Frames(0x06),
Scroll_64Frames(0x01),
Scroll_128Frames(0x02),
Scroll_256Frames(0x03),
VERTICAL_MODE(01),
PAGE_MODE(01),
HORIZONTAL_MODE(02);
/**
*
*/
public byte cmd;
private OLED(int cmd) {
this.cmd = (byte) cmd;
}
}
Listing 16. Enumeration that defines all OLED registers
public enum SSD1306 {
/*=========================================================================
SSD1306 Displays
-----------------------------------------------------------------------
The driver is used in multiple displays (128x64, 128x32, etc.).
=========================================================================*/
Entire_Display_Resume(0xA4),
Entire_Display_On(0xA5),
Normal_Display(0xA6),
Set_Display_Offset(0xD3),
Set_Com_Pins(0xDA),
Set_Vcomh_Deselect_Level(0xDB),
Set_Display_Clock_Div(0xD5),
Set_Precharge_Period(0xD9),
Set_Lower_Column_Start_Address(0x00),
Set_Higher_Column_Start_Address(0x10),
Set_Start_Line(0x40),
Set_Memory_Mode(0x20),
Set_Com_Output_Scan_Direction_Normal(0xC0),
Set_Com_Output_Scan_Direction_Remap(0xC8),
Charge_Pump_Setting(0x8D),
// Scrolling s
SET_VERTICAL_SCROLL_AREA(0xA3),
VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL(0x29),
VERTICAL_AND_LEFT_HORIZONTAL_SCROLL(0x2A);
/**
*
*/
public byte cmd;
private SSD1306(int cmd) {
this.cmd = (byte) cmd;
}
}
Listing 17. Enumeration that defines all the SSD1306 registers
Let's now connect the OLED display to the Raspberry Pi's 3.3 V, GND, MOSI, SCLK, DC (GPIO 24), RESET (GPIO 25), and CS (CE0) pins, as shown in Figure 3, and create a Java ME 8 class called SSD1306Device
with its constructor using the control address and defining the buffer used to store all OLED display data, as shown in Listing 18.
public class SSD1306Device extends SPIRpi {
private GPIOPin reset = null;
private GPIOPin dc = null;
//Buffer for display all oled contents
public ByteBuffer poledbuff = ByteBuffer.wrap(new byte[128 * 64 / 8]);
public SSD1306Device(int address) throws IOException {
super(address);
begin();
}
Listing 18. SSD1306Device
class
The constructor calls the begin()
method to initialize the OLED (see Listing 19) and executes the following actions:
- Defines the GPIO 25 configuration for the reset pin, output only and open, not trigger
- Defines the GPIO 24 configuration for the data/command pin, output only and open, not trigger
- Resets the OLED display using a combination high, low, and high at the reset pin
- Establishes some initial conditions for the OLED display
- Stops all scroll activity
- Clears the display buffer
- Activates the display
private void begin() throws IOException {
// define device for trigger pin
reset = (GPIOPin) DeviceManager.open(new GPIOPinConfig(
0, 25, GPIOPinConfig.DIR_OUTPUT_ONLY,
GPIOPinConfig.MODE_OUTPUT_OPEN_DRAIN,
GPIOPinConfig.TRIGGER_NONE, false));
// define device for echo pin
dc = (GPIOPin) DeviceManager.open(new GPIOPinConfig(
0, 24, GPIOPinConfig.DIR_OUTPUT_ONLY,
GPIOPinConfig.MODE_OUTPUT_OPEN_DRAIN,
GPIOPinConfig.TRIGGER_NONE, false));
// Setup reset pin direction (used by both SPI and I2C)
reset.setValue(true);
// VDD (3.3V) goes high at start, let's just chill for a ms
I2CUtils.I2Cdelay(1);
// bring reset low
reset.setValue(false);
// wait 10ms
I2CUtils.I2Cdelay(10);
// bring out of reset
reset.setValue(true);
sendCommand(OLED.SSD_Display_Off.cmd);
sendCommand(OLED.SSD_Set_Muliplex_Ratio.cmd, 0x3F);
sendCommand(SSD1306.Charge_Pump_Setting.cmd, 0x14);
sendCommand(SSD1306.Set_Memory_Mode.cmd, 0x00);
sendCommand(SSD1306.Set_Display_Clock_Div.cmd, 0x80);
sendCommand(SSD1306.Set_Display_Offset.cmd, 0x00);
sendCommand(SSD1306.Set_Start_Line.cmd | 0x0);
// use this two commands to flip display
sendCommand(OLED.SSD_Set_Segment_Remap.cmd | 0x1);
sendCommand(SSD1306.Set_Com_Output_Scan_Direction_Remap.cmd);
sendCommand(SSD1306.Set_Com_Pins.cmd, 0x12);
sendCommand(SSD1306.Set_Precharge_Period.cmd, 0xF1);
sendCommand(SSD1306.Set_Vcomh_Deselect_Level.cmd, 0x40);
sendCommand(SSD1306.Entire_Display_Resume.cmd);
sendCommand(SSD1306.Normal_Display.cmd);
// Reset to default value in case of
// no reset pin available on OLED,
sendCommand(OLED.SSD_Set_Column_Address.cmd, 0, 127);
sendCommand(OLED.SSD_Set_Page_Address.cmd, 0, 7);
sendCommand(OLED.SSD_Set_ContrastLevel.cmd, 0xCF);
stopscroll();
// Empty uninitialized buffer
clearDisplay();
// turn on oled panel
sendCommand(OLED.SSD_Display_On.cmd);
// wait 100ms
//usleep(100000);
I2CUtils.I2Cdelay(100);
}
Listing 19. Establishing the initial conditions
Then create the following operation methods (see Listing 20):
sendCommand
: Sends a command with one or two attributes
sendData
: Sends data
stopscroll
: Stops all scroll activity
display
: Sends all the contents of the data buffer to the OLED display
clearDisplay
: Clears all the contents in the data buffer
close
: Frees all resources
private void sendCommand(int cmd) throws IOException {
// Set D/C line to low to switch to command mode
dc.setValue(false);
device.write(cmd);
}
private void sendCommand(int cmd, int data) throws IOException {
// Set D/C line to low to switch to command mode
dc.setValue(false);
ByteBuffer sndBuf = ByteBuffer.wrap(new byte[]{(byte) cmd, (byte) data});
device.write(sndBuf);
}
private void sendCommand(int cmd, int data, int data2) throws IOException {
// Set D/C line to low to switch to command mode
dc.setValue(false);
ByteBuffer sndBuf = ByteBuffer.wrap(new byte[]{(byte) cmd,
(byte) data, (byte) data2});
device.write(sndBuf);
}
private void sendData(int cmd) throws IOException {
// Set D/C line to high to switch to data mode
dc.setValue(true);
device.write(cmd);
}
private void stopscroll() throws IOException {
sendCommand(OLED.SSD_Deactivate_Scroll.cmd);
}
public void display() throws IOException {
sendCommand(SSD1306.Set_Lower_Column_Start_Address.cmd); // low col = 0
sendCommand(SSD1306.Set_Higher_Column_Start_Address.cmd); // hi col = 0
sendCommand(SSD1306.Set_Start_Line.cmd); // line #0
// Set D/C line to high to switch to data mode
dc.setValue(true);
// Send all data to OLED
device.write(poledbuff);
}
public void clearDisplay() throws IOException {
poledbuff = ByteBuffer.wrap(new byte[128 * 64 / 8]);
display();
}
public void close() {
try {
reset.close();
dc.close();
device.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Listing 20. Operation methods
Let's now create a test MIDlet class called TestSSD1306
that uses the Gfx library and its basic method drawPixel
to display a single character, a text string, circles, lines, rectangles, and triangles, as shown in Listing 21.
public class TestSSD1306 extends MIDlet {
private LoggingHandler loggerHandler = LoggingHandler.getInstance();
private GfxLib display = null;
private void testdrawcircle() throws IOException {
for (int i = 0; i < display.height(); i += 2) {
display.drawCircle(display.width() / 2,
display.height() / 2, i, GfxLib.WHITE);
display.display();
}
}
public void testfillrect() throws IOException {
int color = 1;
for (int i = 0; i < display.height() / 2; i += 3) {
// alternate colors
display.fillRect(i, i, display.width() - i * 2,
display.height() - i * 2, color % 2);
display.display();
color++;
}
}
public void testdrawtriangle() throws IOException {
for (int i = 0; i < Math.min(display.width(), display.height()) / 2; i += 5) {
display.drawTriangle(display.width() / 2, display.height() / 2 - i,
display.width() / 2 - i, display.height() / 2 + i,
display.width() / 2 + i, display.height() / 2 + i,
GfxLib.WHITE);
display.display();
}
}
public void testfilltriangle() throws IOException {
int color = GfxLib.WHITE;
for (int i = Math.min(display.width(), display.height()) / 2; i > 0; i -= 5) {
display.fillTriangle(display.width() / 2, display.height() / 2 - i,
display.width() / 2 - i, display.height() / 2 + i,
display.width() / 2 + i, display.height() / 2 + i, GfxLib.WHITE);
if (color == GfxLib.WHITE) {
color = GfxLib.BLACK;
} else {
color = GfxLib.WHITE;
}
display.display();
}
}
public void testdrawroundrect() throws IOException {
for (int i = 0; i < display.height() / 2 - 2; i += 2) {
display.drawRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i,
display.height() / 4, GfxLib.WHITE);
display.display();
}
}
public void testfillroundrect() throws IOException {
int color = GfxLib.WHITE;
for (int i = 0; i < display.height() / 2 - 2; i += 2) {
display.fillRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i,
display.height() / 4, color);
if (color == GfxLib.WHITE) {
color = GfxLib.BLACK;
} else {
color = GfxLib.WHITE;
}
display.display();
}
}
public void testdrawrect() throws IOException {
for (int i = 0; i < display.height() / 2; i += 2) {
display.drawRect(i, i, display.width() - 2 * i, display.height() - 2 * i,
GfxLib.WHITE);
display.display();
}
}
public void testdrawline() throws IOException {
for (int i = 0; i < display.width(); i += 4) {
display.drawLine(0, 0, i, display.height() - 1, GfxLib.WHITE);
display.display();
}
for (int i = 0; i < display.height(); i += 4) {
display.drawLine(0, 0, display.width() - 1, i, GfxLib.WHITE);
display.display();
}
I2CUtils.I2Cdelay(3000);
display.clearDisplay();
for (int i = 0; i < display.width(); i += 4) {
display.drawLine(0, display.height() - 1, i, 0, GfxLib.WHITE);
display.display();
}
for (int i = display.height() - 1; i >= 0; i -= 4) {
display.drawLine(0, display.height() - 1, display.width() - 1, i,
GfxLib.WHITE);
display.display();
}
I2CUtils.I2Cdelay(3000);
display.clearDisplay();
for (int i = display.width() - 1; i >= 0; i -= 4) {
display.drawLine(display.width() - 1, display.height() - 1, i, 0,
GfxLib.WHITE);
display.display();
}
for (int i = display.height() - 1; i >= 0; i -= 4) {
display.drawLine(display.width() - 1, display.height() - 1, 0, i,
GfxLib.WHITE);
display.display();
}
I2CUtils.I2Cdelay(3000);
display.clearDisplay();
for (int i = 0; i < display.height(); i += 4) {
display.drawLine(display.width() - 1, 0, 0, i, GfxLib.WHITE);
display.display();
}
for (int i = 0; i < display.width(); i += 4) {
display.drawLine(display.width() - 1, 0, i, display.height() - 1,
GfxLib.WHITE);
display.display();
}
I2CUtils.I2Cdelay(3000);
}
private void waitAndClear() throws IOException {
I2CUtils.I2Cdelay(3000);
display.clearDisplay();
}
@Override
public void startApp() {
...
display = new GfxLib(0);
Logger.getGlobal().log(Level.INFO, "OLED Init");
display.drawChar(5, 5, (byte) 65, 1, 0, 1);
display.display();
display.drawChar(5, 20, (byte) 66, 1, 0, 2);
display.display();
waitAndClear();
display.setTextSize(1);
display.setTextColor(GfxLib.WHITE);
display.setCursor(0, 0);
display.print("Test Oled!\n");
display.setTextSize(1);
display.setTextColor(GfxLib.BLACK, GfxLib.WHITE);
display.print("Java ME is Great!\n");
display.display();
waitAndClear();
testdrawcircle();
waitAndClear();
testdrawtriangle();
waitAndClear();
testdrawline();
waitAndClear();
testdrawrect();
waitAndClear();
testfillrect();
waitAndClear();
testfilltriangle();
waitAndClear();
testdrawroundrect();
waitAndClear();
testfillroundrect();
waitAndClear();
...
}
@Override
public void destroyApp(boolean unconditional) {
display.close();
}
}
Listing 21. Test MIDlet class for the OLED display
Performing Some Additional Configuration
Before running our test MIDlets using NetBeans IDE 8.0.2, it is important to establish the appropriate API permissions. To do this, in the IDE, select project JavaMEDemos, and then right-click and select Properties to show the Project Properties window. Select Application Descriptor, and then select the API Permissions tab. Include the following permission, as shown in Figure 4:
jdk.dio.spibus.SPIPermission *:* , open

Figure 4. Establishing API permissions
Conclusion
This is the last part of this series, which gave examples of how to use different types of devices with the Raspberry Pi, from GPIO devices to I2C, UART, and SPI devices. Each article in the series presented many possibilities for connecting sensors and devices to the Raspberry Pi and then using Java ME 8 to control them.
To build on this article, you can connect different analog devices to the MCP3008 channels (remember, it has eight channels). For example, you could connect an analog temperature sensor, such as the TMP35, and light-dependent resistors (LDRs), and then read their values with the Raspberry Pi SPI bus.
If you experiment with connecting other devices, remember to define an enumeration that defines all the commands for each device and a Java ME 8 class that implements all the device's logical rules (gleaned from the device's technical documentation), such as the different values that can be entered into the device's registers to obtain sensor values or operate the device.
Java ME 8 + Raspberry Pi +Sensors = IoT World (Part 1)
About the Author
Jose Cruz is a software engineer who has been working with Java since 1998. He is a lead developer of Java, Java ME, and Java EE at Ferreteria EPA C.A. in Venezuela. From an early age, his hobby has been electronics. This has led him to combine computing with electronics and develop projects where Java and embedded devices such as Arduino and Raspberry Pi are used.
Join the Conversation
Join the Java community conversation on Facebook, Twitter, and the Oracle Java Blog!