Java ME 8 + Raspberry Pi + Sensors = IoT World (Part 4)

Version 10

    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.0
    spi 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.

    f1.gif

    Figure 1. Block diagram of the circuits we will create

    f2.png

     

    Figure 2. Components we will use

    f3.png

     

    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
    

     

    image.png

    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 3)

    Java ME 8 + Raspberry Pi + Sensors = IoT World (Part 2)

    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!