/** * Global Sensor Networks (GSN) Source Code * Copyright (c) 2006-2014, Ecole Polytechnique Federale de Lausanne (EPFL) * * This file is part of GSN. * * GSN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GSN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GSN. If not, see <http://www.gnu.org/licenses/>. * * File: gsn-tiny/src/tinygsn/model/wrappers/utils/MICSensor.java * * @author Do Ngoc Hoan */ package tinygsn.model.wrappers.utils; import java.io.Serializable; import java.util.Arrays; import tinygsn.beans.DataField; import tinygsn.beans.StaticData; import tinygsn.beans.StreamElement; import android.content.Context; import android.os.Handler; import android.widget.Toast; import ch.serverbox.android.ftdiusb.FTDI_USB_Handler; /* * All the communication with the sensor happens here. */ public class MICSensor extends FTDI_USB_Handler { private static MICSensor instance = null; public static MICSensor getInstance(){ if (instance == null){ instance = new MICSensor(StaticData.globalContext); } return instance; } public interface ReadyListener { public void onReady(); } public interface VirtualSensorDataListener { public void consume(StreamElement se); } private VirtualSensorDataListener listener = null; private ReadyListener initListener = null; private DataField[] df; // Calibration values private double k = 0.025; private double a0 = 0.7; private double a1 = 0.02; private double b0 = 0.7; private double b1 = 0.02; private int interval = 30; // Create an handler for drawing toast messages from other threads etc. private Handler handler; // Boolean flags to check for sensor answers etc private boolean waitForDiagnostic; // private boolean waitForAuto; private boolean waitForStop; // private boolean waitForTimer; private boolean measurementReceived; private boolean sensorInitialized; protected boolean saveToFile; private boolean autoMeasurementThreadFlag; private boolean initThreadFlag; private boolean stopThreadFlag; private char[] receiveBuffer; // A buffer to store the received bytes private int bufferIndex; // The buffer index for the receive buffer private MICSensor(Context a) { super(a); try { df = new DataField[6]; df[0] = new DataField("resistanceO", "int"); df[1] = new DataField("resistanceV", "int"); df[2] = new DataField("humidity", "double"); df[3] = new DataField("temperature", "double"); df[4] = new DataField("ozoneCalibrated", "double"); df[5] = new DataField("vocCalibrated", "double"); } catch (Exception e) { } handler = new Handler(); // Initialize the receive buffer bufferIndex = 0; receiveBuffer = new char[32]; // 32 is the largest string size for the // sensor. waitForDiagnostic = false; measurementReceived = false; sensorInitialized = false; autoMeasurementThreadFlag = false; initThreadFlag = false; stopThreadFlag = false; } public void setListener(VirtualSensorDataListener listener) { this.listener = listener; } public void setInitListener(ReadyListener initListener) { this.initListener = initListener; } public DataField[] getDataField() { return df; } /* * Toggle automatic measurements. The measurements are polled in the interval * specified in the preferences. Handled in its own thread so it is * non-blocking. */ public boolean toggleAutoMeasurements(boolean isChecked) { Thread thread = new Thread() { public void run() { while (autoMeasurementThreadFlag) { getMeasurement(); try { // Sleep for interval seconds. sleep(interval * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // If no answer was received, wait a bit longer. // If we try to get a reading when the sensor is updating itself, it // doesn't respond. if (measurementReceived == false) { try { sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; // Start the auto update thread when the button is on. if (isChecked) { // If the sensor couldn't initialize, we don't start the thread. /* * if(sensorInitialized==false) { Toast.makeText(activity, * "Sensor not initialized", Toast.LENGTH_SHORT).show(); // Return false * to indicate that the button has to be turned off again. * * return false; } else { */ autoMeasurementThreadFlag = true; thread.start(); // } } else { autoMeasurementThreadFlag = false; } return true; } /* * Send a {M} to get a measurement from the sensor. The received data is * handled in the onDataReceived routine. */ public void getMeasurement() { if (sensorInitialized == false) { // Toast // .makeText(activity, "Sensor not yet initialized", Toast.LENGTH_SHORT) // .show(); initSensor(); return; } measurementReceived = false; write("{M}\n".getBytes()); } /* * Process a read input string. */ private void processInput(String receivedString, int length) { // Valid strings start with { and end with }. Anything else is discarded. if (receivedString.charAt(0) != '{' || receivedString.charAt(length - 1) != '}') { return; } // A diagnostic message is received. // {A00}->OK if (receivedString.charAt(1) == 'A' && receivedString.charAt(2) == '0' && receivedString.charAt(3) == '0') { waitForDiagnostic = false; } else if (receivedString.charAt(1) == 'Q') { waitForStop = false; } // If it is a measurement, extract the values. // A correct measurement always has length 18. else if (receivedString.charAt(1) == 'M') { measurementReceived = true; // Convert the received string to hex values. String[] values = receivedString.split("[,|}|{M]"); // split string: // pos 1: temp // pos 2: humidity // pos 3: resistance VOC // pos 4: resistance O3 int resistanceV = Integer.parseInt(values[4]); int resistanceO = Integer.parseInt(values[5]); double temperature = Double.parseDouble(values[2]); double humidity = Double.parseDouble(values[3]); double resistanceCompensatedO = resistanceO * Math.exp(k * ((temperature - 25))); double resistanceCompensatedV = resistanceV * Math.exp(k * ((temperature - 25))); double ozoneCalculated = a0 + a1 * resistanceCompensatedO; double vocCalculated = b0 + b1 * resistanceCompensatedV; StreamElement se = new StreamElement(df, new Serializable[] { resistanceO, resistanceV, humidity, temperature, ozoneCalculated, vocCalculated }); if (listener != null) { listener.consume(se); } } } /* * Initialize the sensor. Do it in its own thread, to avoid blocking. {A}, * {S}, {W...} is sent sequentially. After every step we wait for the correct * answer. If no answer is received for 1 second, the init thread is aborted. */ public void initSensor() { // The thread which sends the commands and waits for the answers from the // sensor. Runnable runnable = new Runnable() { public void run() { if (initThreadFlag) return; int counter = 0; // Set the thread flag. initThreadFlag = true; // Wait for a diagnostics message. waitForDiagnostic = true; // Send a {A} to get a diagnostics answer from the sensor. // The sensor should send a {A00} back. write("{A}\n".getBytes()); /*handler.post(new Runnable() { public void run() { Toast.makeText(activity.getApplicationContext(), "Initialization started", Toast.LENGTH_SHORT).show(); } });*/ l("diagnostic sent"); while (waitForDiagnostic == true) { try { Thread.sleep(100); } catch (InterruptedException e) { } counter++; if (counter > 600) { initThreadFlag = false; initToast(false); return; } } // Successfully initialized. sensorInitialized = true; // Clear the thread flag. initThreadFlag = false; initToast(true); // showLog("Successfully initialized."); } private void initToast(boolean success) { if (success) { handler.post(new Runnable() { public void run() { if (initListener != null) initListener.onReady(); //Toast.makeText(activity.getApplicationContext(),"Initialization succeded", Toast.LENGTH_SHORT).show(); } }); } /*else { handler.post(new Runnable() { public void run() { Toast.makeText(activity.getApplicationContext(), "Initialization failed", Toast.LENGTH_SHORT).show(); } }); }*/ } }; // Start the sensor init thread. if (initThreadFlag == false) { new Thread(runnable).start(); } else { // Toast.makeText(activity, "Already initializing", Toast.LENGTH_SHORT) // .show(); } } /* * Pause the sensor. The sensor has to be reinitialized after that */ public void Pause() { // The thread which sends the commands and waits for the answers from the // sensor. Runnable runnable = new Runnable() { public void run() { int counter = 0; // Set the thread flag. stopThreadFlag = true; // Wait for a diagnostics message. waitForStop = true; // Send a {A} to get a diagnostics answer from the sensor. // The sensor should send a {A00} back. write("{Q}\n".getBytes()); handler.post(new Runnable() { public void run() { Toast.makeText(activity.getApplicationContext(), "Sensor stopped", Toast.LENGTH_SHORT).show(); } }); l("stop sent"); while (waitForStop == true) { try { Thread.sleep(100); } catch (InterruptedException e) { } counter++; if (counter > 50) { stopThreadFlag = false; return; } } // Successfully uninitialized. sensorInitialized = false; // Clear the thread flag. stopThreadFlag = false; } }; // Start the sensor init thread. if (stopThreadFlag == false) { new Thread(runnable).start(); } else { Toast.makeText(activity, "Already uninitializing", Toast.LENGTH_SHORT) .show(); } } /* * Process any data that's coming from the usb port. */ @Override protected void onDataReceived(final byte[] buffer, final int size) { new Thread(new Runnable() { public void run() { l("receive:" + new String(buffer, 0, size)); // showLog("onDataReceived " + "receive:" + new String(buffer, 0, // size)); if (receiveBuffer != null) { for (int i = 0; i < size; i++) { if ((char) buffer[i] == '{') { bufferIndex = 0; Arrays.fill(receiveBuffer, (char) 0); } // Write the read byte into the receive buffer. receiveBuffer[bufferIndex] = (char) buffer[i]; // If a } is received, process the read data. if (receiveBuffer[bufferIndex] == '}') { processInput(new String(receiveBuffer), bufferIndex + 1); // Clear the buffer. bufferIndex = 0; Arrays.fill(receiveBuffer, (char) 0); } else { // Update the buffer index. Don't let it overflow. bufferIndex = bufferIndex >= receiveBuffer.length - 1 ? receiveBuffer.length - 1 : bufferIndex + 1; } } } } }).start(); } // void showLog(final String text) { // activity.runOnUiThread(new Runnable() { // public void run() { // Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); // } // }); // } }