Friday, March 22, 2013

Publishing Data from a Beaglebone with a Java Web Service


I have been experimenting with Java on the Beaglebone. Below are some notes on a simple SOAP-based Web service that returns the current temperature from a TMP102 sensor. A RESTful Web service would probably be even simpler, but I wanted to try it with SOAP.

Looking at material on the Web, I was unclear about how difficult it would be to use Java to read data from the thermometer using I2C. It didn't look very straight forward, so for this round, I decided just to launch a shell from Java and run the Python code I posted before that reads the temperature from a TMP102.  This isn't the most efficient way, but it actually runs quite well for for my use in a low traffic situation.

The RPC-style Web service has just 2 methods, getTempC and getTempF, to report the current temperature in degrees Celsius or degrees Fahrenheit.  Both methods return a float.

I have experimented with running Tomcat 7 on my Beaglebone, and it definitely works, but this sample Web service just uses its own very simple class to publish the service. The publisher is only single threaded, but again the performance is quite adequate for my needs. The publisher could certainly be rewritten to be multithreaded - or one could run an application server like Tomcat.


Java


The first step was to install Java. As of March, 2913, it looks like only Java 6 is available in packages for
Ångström, but this example doesn't need any Java 7 features.

Use opkg to install the following packages:

openjdk-6-java 
openjdk-6-jdk


Code


I wrote and compiled the code on my desktop computer in Netbeans. I then used SCP or WinSCP to copy the resulting JAR file to the Beaglebone for deployment.

Web Service Interface


package tempservice;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface TempService {
    @WebMethod public float getTempC() throws java.io.IOException, 
     java.lang.InterruptedException;
    @WebMethod public float getTempF() throws java.io.IOException, 
     java.lang.InterruptedException;
}

Service Implementation Class


import java.io.IOException;
import java.io.InputStreamReader;
import javax.jws.WebService;

@WebService(endpointInterface = "tempservice.TempService")
public class TempServiceImpl implements TempService {
    @Override
    public float getTempC() throws java.io.IOException, 
     java.lang.InterruptedException {
        // Get runtime
        java.lang.Runtime rt = java.lang.Runtime.getRuntime();
        // Start a new process & run python
        // Use Python to access TMP102 sensor
        java.lang.Process p = rt.exec("/usr/bin/python /home/root/tmp102.py");
        // wait for the process to complete
        p.waitFor();
        // Get process output - its InputStream
        java.io.InputStream is = p.getInputStream();
        java.io.BufferedReader reader = new java.io.BufferedReader(new InputStreamReader(is));
        // And print each line
        String s = null;
        s = reader.readLine();
        return Float.parseFloat(s);
    }

    @Override
    public float getTempF() throws IOException, InterruptedException {
        float c = getTempC();
        return ((c / 5) * 9) + 32;
    }
}

Web Service Publisher Class

package tempservice;

import javax.xml.ws.Endpoint;

public class TempServicePublisher {
    public static void main(String[] args) {
        // 1st argument is the publication URL       
        // 2nd argument is an SIB instance       
        Endpoint.publish("http://192.168.0.106:9876/temp", new TempServiceImpl());
    } 


Starting the service with the command  java -jar TempService.jar > /media/sdhc1/TempSrvcLog.log 2>&1, but the process died every time I closed my session in SSH. Just try to place the process in the background didn't work, nor did any of my attempts with nohup or start-stop-daemon. In the end, I logged in using screen and started the service that way. 

SoapUI is a good way to test the service and see what's happening at the message level. I have also written a quick and easy client in Python 2 that I run from a Raspberry Pi (though the code should run on anything that supports Python).

Friday, March 15, 2013

Using an ePIR Motion Detector with a Maple Mini

The Zilog ePIR module is an easy-to-use motion detector. Using the the serial communication mode, the controller polls the deice at regular intervals by sending a single command character. The ePIR responds with one character: Y if motion has been detected and N if no motion has been detected.


Connections


Pin 1 on the ePIR is marked '1'.

ePIR    Maple Mini
1       GND
2       VCC
3       TX1 (26) - Serial1
4       RX1 (25) - Serial1 (with 10k pull-up resistor)
6       GND


Code


The following code checks for motion once per second and, for this example, prints the status to the SerialUSB monitor.

static const char STATUS_CMD = 'a';
static const int DELAY = 1000;

static char getch() {
  // Wait if ePIR not avail. or busy
  while (!Serial1.available())
    ;
  return Serial1.read();
}

void setup() {
  Serial1.begin(9600);
}

void loop() {
  Serial1.print(STATUS_CMD);
  SerialUSB.println(getch()); 
  delay(DELAY);




Tuesday, March 5, 2013

Using the 4x4 Universal 16 Key Keypad for Arduino with a Maple Mini


The listings at Amazon and other online vendors show this inexpensive membrane keypad as "4x4 Universial 16 Key Switch Keypad Keyboard For Arduino." There was no documentation for the product, nor were there any links from Amazon. The post about this on my Arduino/Netduino blog has proven very popular, so I wanted to see if I could get it to work with my Maple Mini. I have found that it and the Arduino Keypad library work very well with the Maple Mini with just a couple small changes.

The arrangement of the keys is

1 2 3 A 
4 5 6 B 
7 8 9 C 
* 0 # D 

Connections:


There is a ribbon with 8 wires running from the bottom of the keypad. With the keypad face upand the connector ribbon pointing downward, the wires connect in sequence from left to right to Maple Mini pins 0 - 7. 


Keypad Library


The Arduino Keypad library is available from the Arduino Playground. [Note: Ver. 3.0 of the Keypad library works, but 3.1 doesn't.]  The library requires only one small change in the Keypad.h file:

Change line 75 from 

virtual void pin_mode(byte pinNum, byte mode) { pinMode(pinNum, mode); }


to

virtual void pin_mode(byte pinNum, WiringPinMode mode) { pinMode(pinNum, mode); }

With that change made, the following sample code prints the key pressed to the SerialUSB console.


Code:


#include <Keypad.h>

const byte ROWS = 4; 
const byte COLS = 4; 
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {0,1,2,3}; //connect to row pinouts 
byte colPins[COLS] = {4,5,6,7}; //connect to column pinouts

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  
}

void loop(){
  char key = keypad.getKey();

  if (key != NO_KEY){
    SerialUSB.println(key);
  }
}

Monday, March 4, 2013

Using a Parallax Serial RFID Reader with a Maple Mini

The Parallax RFID Reader (Serial Version) provides an easy way to read 125 kHz / EM41000 RFID tags, and it works nicely with the Maple Mini. The following very basic example reads a tag when it is brought near the reader and prints the tag's ID number to the SerialUSB console.

Note that the Parallax RFID reader is a 5v device. I am using USB power and have its VCC connected to the Maple Mini's VIN.  The GPIO pins used are 5-volt tolerant.

Connections:

Parallax RFID   Maple Mini
GND             GND
SOUT            RX1 (Pin 25)
/ENABLE         Pin 2
VCC             VIN

Code:


int ENABLE_PIN = 2;
int  val = 0; 
char code[10]; 
int bytesread = 0; 

void setup() {
  Serial1.begin(2400);  // Reader connects at 2400 baud
  pinMode(ENABLE_PIN, OUTPUT);
  digitalWrite(ENABLE_PIN, LOW);  // Activate RFID reader
}

void loop() {
  if(Serial1.available() > 0) {
    if((val = Serial1.read()) == 10) {     // Check for header char 
      bytesread = 0; 
      while(bytesread < 10) {              // Read 10-digit code 
        if( Serial1.available() > 0) { 
          val = Serial1.read(); 
          if((val == 10)||(val == 13)) {   // If header/stop bytes  
            break;                         // stop reading 
          } 
          code[bytesread] = val;           // Store the digit           
          bytesread++;                     // Ready to read next digit  
        } 
      } 
      if(bytesread == 10) {                // If 10-digit read is complete 
        SerialUSB.print("TAG code is: ");  // possibly a good tag 
        SerialUSB.println(code);           // Print the tag's ID code 
      } 
      bytesread = 0; 
      digitalWrite(ENABLE_PIN, HIGH);      // Pause to avoid flooding
      delay(1500);                         // Wait for a bit 
      digitalWrite(ENABLE_PIN, LOW);       // Activate the RFID reader
    }        
  } 
}


Reading Time & Date from a Chronodot (DS3231) with a Maple Mini

This simple example shows how to read the current date and time from a Chronodot v. 2.1 real-time clock module (based on the DS3231 chip). The Chronodot connects to the Maple Mini via I2C (using the Wire library).

The sample below reads the current date and time from the Chronodot, does some simple formatting, and prints the result to the USBSerial console.

Connections:

Chronodot      Maple Mini
GND            GND
VCC            VCC
SCL            I2C1 SCL (pin 16) - with 10k Ohm pull-up resistor
SDA            I2C1 SDA (pin 15) - with 10k Ohm pull-up resistor

Code:

#include <Wire.h>

int addr = 0x68; // 0x68 is DS3231 I2C device address

void setup(){
  Wire.begin(15, 16); // Connected to i2c1
}

void loop() {
  // Send request to receive data starting at register 0
  Wire.beginTransmission(addr); 
  Wire.send(0);                 // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(addr, 7);    // request 7 bytes (seconds, minutes, hours, day, 
                                // date, month, year)
  while(Wire.available())
  { 
    int seconds = bcdToDec(Wire.receive()); 
    int minutes = bcdToDec(Wire.receive()); 
    int hours = bcdToDec(Wire.receive());   
    int day = bcdToDec(Wire.receive());    // Not used in example
    int date = bcdToDec(Wire.receive());   // Day of month
    int month = bcdToDec(Wire.receive());
    int year = bcdToDec(Wire.receive());
    printDateTime(month, date, year, hours, minutes, seconds);
    delay(1000);
  }
}

void printDateTime(int month, int date, int year, int hours, 
    int minutes, int seconds) 
{
    if(month < 10)
      SerialUSB.print('0');
    SerialUSB.print(month);  
    SerialUSB.print('/');
    if(date < 10)
      SerialUSB.print('0');   
    SerialUSB.print(date);
    SerialUSB.print('/');
    SerialUSB.print(year);
    SerialUSB.print(' '); 
    if(hours < 10)
      SerialUSB.print('0');   
    SerialUSB.print(hours);   
    SerialUSB.print(':');
    if(minutes < 10)
      SerialUSB.print('0');    
    SerialUSB.print(minutes);
    SerialUSB.print(':');
    if(seconds < 10)
      SerialUSB.print('0');
    SerialUSB.println(seconds);  
}

// Convert binary coded decimal to decimal
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

Saturday, March 2, 2013

Beaglebone & TMP102 Temperature Sensor

The notes and sample below show how to get the Sparkfun TMP102 breakout board working with the Beaglebone. The TMP102 connects to the beaglebone using I2C, and the breakout board includes pull-up resistors on the SDA and SCL lines.

This example is in Python.

The Ångström Linux distribution that comes with the Beaglebone includes the I2C tools (such as i2cdetect). You will, however, need to install the python-smbus module. Be sure to run opkg update first, and then  run the following command to install the python-smbus package: opkg install python-smbus.

Connections:


TMP102 Breakout Beaglebone
V+              P9 Pin 3
SDA             P9 Pin 20
SCL             P9 Pin 19
GND             P9 Pin 1
ADD0            Connect to GND on breadboard for I2C addr. 0x48 

Connecting ADD0 to V+ sets the I2C address to 0x49. Connecting it to SDA sets the board's address to 0x4a and connecting it to SCL results in setting the address to 0x4b. Adjust the code below accordingly.

Code:


# Read temp in degrees C from tmp102 breakout board by Sparkfun
import smbus
import time

# Beaglebone uses I2C bus 3
bus = smbus.SMBus(3)
# Read block of I2C data
data = bus.read_i2c_block_data(0x48, 0)
msb = data[0]
lsb = data[1]
print (((msb << 8) | lsb) >> 4) * 0.0625