Appendix D. Code examples

The SensorConnection represents a connection to the real sensor device. After having been opened, the SensorConnection can be used to get the data from the sensor. The SensorConnection is a Generic Conncetion Framework (GCF) Connection. The following code example illustrates how to start synchronous data fetching by first opening the sensor by giving a sensor URL as a parameter, and then calling method getData() .

    SensorConnection thermometer = 
        (SensorConnection)Connector.open("sensor:temperature");
    Data[] data = thermometer.getData(1);
   

The measured data is returned as an array of Data objects, where each Data object encapsulates the data of one channel. The channel is a way to structure the data. If the sensor is measuring just one property, there is only one channel. This is the case, for example, with the thermometer. The length of the returned Data object array with this sensor is always one. The measured value can be obtained in the following way:

    double value = data[0].getDoubleValues()[0];

However, a 3D compass has three channels:

   
    SensorConnection compass = 
        (SensorConnection)Connector.open("sensor:direction");
    Data[] data = compass.getData(1);
    for (int i=0, l=data.lenght; i<l; i++){
         double value = data[i].getDoubleValues()[0];
         System.out.println("channel = "+
         data[i].getChannelInfo().getName()+", value=" + value);
    }

Sample output:

    channel = heading, value = 108.1
    channel = pitch, value = 5.2
    channel = roll, value = -10.3

In addition to getting the data, it is possible to monitor it. Various conditions - limits and ranges - can be set. When the condition is met, the application implementing the ConditionListener interface is notified. The conditions are defined separately for each channel.

The SensorManager manages sensors. It has two tasks:

  1. finding the desired sensor

  2. listening for the availability of a given sensor

Searching for a sensor can be based on a quantity, and/or a context type, or the sensor URL. The quantity defines what the sensor is measuring, for example: temperature, humidity, or direction. The context type defines where the measurement is taken. The valid values for the context type are listed below:

The returned SensorInfo array lists the found sensors. The SensorInfo encapsulates sensor specific information such as the model, the version, and the channels of the sensor.

The SensorManager periodically checks the registered sensors and notifies their registered SensorListener objects of changes in the availability of the sensor. Also the MIDP PushRegistry needs to periodically check the registered sensors. Registration for the PushRegistry is based on a sensor URL. The URL offers two alternatives:

  1. listening for the availability of the sensor

  2. monitoring the data of the sensor (limit and range conditions)

When a sensor becomes available, or one of the set conditions is met, the registered sensor application is launched.

The Sniffer MIDlet below demonstrates the use of Mobile Sensor API . The MIDlet finds every sensor of the device and shows how to get data synchronously, asynchronously, and using conditions. Imported classes and comments are omitted for brevity.

public class Sniffer extends MIDlet implements Runnable,
CommandListener, DataListener, ConditionListener {
    private Display display;
    private Form form;
    private boolean isPaused;
    private Command nextCommand = new Command("Next sensor",Command.ITEM,1);
    private Command exitCommand = new Command("Exit",Command.EXIT,1);
    private static Data[] data;
    private static SensorConnection sensor = null;
    private static SensorInfo[] infos;
    private static int index = 0;
    private static boolean isReady = false;
    private StringItem syncStringItem;
    private StringItem asyncStringItem;
    private StringItem condStringItem;
    private StringItem sensorStringItem;
    private static final int BUFFER_SIZE = 10;

    private static final ObjectCondition COND =
        new ObjectCondition(
            new Object(){public boolean equals(Object o){return true;}});
    private static final LimitCondition COND1 =
        new LimitCondition(0, Condition.OP_GREATER_THAN_OR_EQUALS);
    private static final LimitCondition COND2 =
        new LimitCondition(0, Condition.OP_LESS_THAN_OR_EQUALS);

    public Sniffer() {
        display = Display.getDisplay(this);
        form = new Form("Sensor Sniffer");
        form.addCommand(nextCommand);
        form.addCommand(exitCommand);
        form.setCommandListener(this);
        sensorStringItem = new StringItem("Sensor","");
        form.append(sensorStringItem);
        syncStringItem = new StringItem("Sync","");
        form.append(syncStringItem);
        asyncStringItem = new StringItem("Async" , "");
        form.append(asyncStringItem);
        condStringItem = new StringItem("Cond" , "");
        form.append(condStringItem);
        display.setCurrent(form);
        infos = SensorManager.findSensors(null, null);
        readSensor(index);
        index = (index>=infos.length-1)? 0 : index+1;
    }
    public void startApp() { isPaused = false; }
    
    public void commandAction(Command command, Displayable screen) {
        if (isPaused()) return;
        // next command
        if (command == nextCommand) {
            readSensor(index);
            index = (index>=infos.length-1)? 0 : index+1;
            return;
        }
        // exit command
        if (command == exitCommand){
            destroyApp(false);
            notifyDestroyed();
        }
    }

    public void conditionMet(SensorConnection sensor, Data d,
                             Condition condition) {
        data = new Data[]{d};
        setReady(true);
    }
    
    public void dataReceived(SensorConnection sensor, Data[] d,
                             boolean isDataLost) {
        data = d;
        setReady(true);
    }
    
    public void destroyApp(boolean par) {isPaused = true;}

    public boolean isPaused() {return isPaused;}
    
    public synchronized boolean isReady() {return isReady;}

    public void pauseApp() {isPaused = true;}

    private void print(StringItem stringItem, Data[] d){
        StringBuffer channelBuffer = new StringBuffer();
        for (int i=0, l=d.length; i<l; i++){
            ChannelInfo cInfo = d[i].getChannelInfo();
            int dataType = cInfo.getDataType();
            String valueString="";
            switch(dataType){
                case ChannelInfo.TYPE_DOUBLE:
                {
                    double[] values = d[i].getDoubleValues();
                    int l2 = values.length;
                    valueString = l2 > 0 ?
                        resolved(cInfo, values[l2-1]) : null;
                    break;
                }
                case ChannelInfo.TYPE_INT:
                {
                    int[] values = d[i].getIntValues();
                    int l2 = values.length;
                    valueString = l2 > 0 ?
                        resolved(cInfo, values[l2-1]) : null;
                    break;
                }
                case ChannelInfo.TYPE_OBJECT:
                {
                    Object[] values = d[i].getObjectValues();
                    int l2 = values.length;
                    valueString = l2 > 0 ?
                        values[l2-1].toString() : null;
                    break;
                }
            }
            String channelString =
                cInfo.getName()+": "+valueString + "\n";
            channelBuffer.append(channelString);
            System.out.println(" "+channelString);
        }
        stringItem.setText(channelBuffer.toString());
    }
    
    private String resolved(ChannelInfo channelInfo, double value){
        int scalingFactor = channelInfo.getScale();
        double resolver = 1;
        for (int i=0,l=Math.abs(scalingFactor); i<l; i++){
            resolver = scalingFactor>0? resolver*10 : resolver/10 ;
        }
        return "" + value * resolver + " " + channelInfo.getUnit();
    }

    //Async/sync data retrieval and condition mechanism demonstrated
    public void run() {
        // sync
        try{
            data = sensor.getData(BUFFER_SIZE);
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("Couldn't read data from the sensor "+
                sensor.getSensorInfo().getUrl());
        }
        print(syncStringItem, data);
        setReady(false);
        
        // async
        sensor.setDataListener(this, BUFFER_SIZE);
        while(!isReady()){
            try{
                wait();
            }catch(Exception e){}
        }
        sensor.removeDataListener();
        print(asyncStringItem, data);
        setReady(false);
        
        // condition
        addConditions();
        while(!isReady()){
            try{
                wait();
            }catch(Exception e){}
        }
        removeConditions();
        print(condStringItem, data);
        setReady(false);
        
        try{
            sensor.close();
        }catch(Exception e2){
            e2.printStackTrace();
            System.out.println("Exception while closing the sensor");
        }
    }
    
    public void readSensor(int index){
        if (infos.length<index-1) return;
        SensorInfo info = infos[index];
        String url = info.getUrl();
        System.out.println("Sensor: "+url);
        sensorStringItem.setText(url);
        syncStringItem.setText("");
        asyncStringItem.setText("");
        condStringItem.setText("");
        try{
            sensor = (SensorConnection)Connector.open(url);
        }catch(SecurityException se){
            System.out.println("No permission to use the sensor!");
            return;
        }
        catch(Exception e){
            e.printStackTrace();
            System.out.println("Couldn't open sensor " + url);
            return;
        }
        new Thread(this).start();
    }
    
    private void addConditions(){
        ChannelInfo[] cInfos = sensor.getSensorInfo().getChannelInfos();
        for (int i=0,l=cInfos.length; i<l; i++){
            Channel channel = sensor.getChannel(cInfos[i]);
            if (cInfos[i].getDataType() == ChannelInfo.TYPE_OBJECT){
                channel.addCondition(this, COND);
                continue;
            }
            channel.addCondition(this, COND1);
            channel.addCondition(this, COND2);
        }
    }

    private void removeConditions(){
        ChannelInfo[] cInfos = sensor.getSensorInfo().getChannelInfos();
        for (int i=0,l=cInfos.length; i<l; i++){
            Channel channel = sensor.getChannel(cInfos[i]);
            channel.removeAllConditions();
        }
    }
        
    private synchronized void setReady(boolean isReady){
        Sniffer.isReady = isReady;
        notify();
    }
}

In the constructor the row:

        infos = SensorManager.findSensors(null, null);

finds all the sensors and assigns them to the variable infos . The "Find sensor" command handles one sensor at a time and then increments the index by 1. All the found sensors can be handled in sequence by pressing the "Find sensor" button in a row. The index is reset to value of 0 after all the sensors are handled, hence the loop is never-ending.

In the condition-based data retrieval mode the first data value is ensured to meet the condition. If the data type of the channel is numeric (int/double) then two overlapping LimitCondition objects are set. Those two conditions cover the whole range of values. If the data type of the channel is TYPE_OBJECT then a special anonymous Object is used as a limit. Its equals() method is overridden to return always true .