package com.geeksville.location;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Observable;
import java.util.Set;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.location.Location;
import android.util.Log;
import android.widget.Toast;
/**
* A client for scott@cnes.com bluetooth vario
*
* @author kevinh
*
* On Mon, Feb 6, 2012 at 11:47 AM, Scott Jepson <scott@cnes.com> wrote:
*
* Hi Kevin, So here's the output from the bluetooth barometer:
* printf("V%06ld A%07ld P%06ld B%03ld\r\n",Vspd,Alt,Pressure,**Battery)
*
* Vspd=CM/S and looks like 000005 or -00005 Alt=CM and looks like
* 0038002 which would be 380.02 Meters Pressure=Pa and looks like
* 096840 Battery=Volts and looks like 032 which would be 3.2 volts
*
* So the whole output looks like: V000005 A0038002 P096840 B032
*
* How fast do you want the data samples sent? So far I can do 25
* samples a second.
*
*
* -Scott
*/
public class CNESBarometerClient extends Observable implements
IBarometerClient, Runnable {
private static final String TAG = "BluetoothBarometerClient";
// / What device name do we look for?
private static final String myName = "BlueBaro";
// private static final int myClass = 0xa01; // See
// http://developer.android.com/reference/android/bluetooth/BluetoothClass.Device.html,
// for now I'm guessing at a
// value
/** A unique ID for our app */
private UUID uuid = UUID.fromString("b00d0c47-899b-4484-810a-5b27a514e906");
private BluetoothDevice device;
private Thread thread;
private float altitude, vspd, batVoltage, pressure;
// / true if we've been set based on the GPS
private boolean isCalibrated = false;
public CNESBarometerClient(Context context) {
this.device = findDevice();
// We do all the real work in a background thread, so we don't stall and can
// handle reboots of the bluetooth device
// FIXME this burns too much power, we should instead only create our reader
// thread in addObserver, then
// shut it down gracefully when the number of observers drops to zero. This
// will have the nice effect of only talking
// to the bluetooth baro when we actually need its data.
thread = new Thread(this, "BluetoothBaro");
thread.setDaemon(true);
thread.start();
}
static boolean isAvailable() {
return findDevice() != null;
}
public String getStatus() {
return "CNES";
}
private static BluetoothDevice findDevice() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.isEnabled()) {
Set<BluetoothDevice> pairedDevices = adapter.getBondedDevices();
for (BluetoothDevice device : pairedDevices) {
Log.d(TAG,
"Considering " + device.getName() + "@" + device.getAddress());
// if (device.getBluetoothClass().getDeviceClass() == myClass) return
// device;
if (device.getName().startsWith(myName))
return device;
}
}
return null;
}
@Override
public void setAltitude(float meters, Calibration calibration) {
// FIXME - apply correction from GPS based altitude
}
public float getPressure() {
return pressure;
}
@Override
public float getAltitude() {
return altitude;
}
@Override
public float getVerticalSpeed() {
return vspd;
}
public float getBattery() {
return batVoltage;
}
public float getBatteryPercent() {
return Float.NaN; //FIXME
}
@Override
public void improveLocation(Location l) {
if (isCalibrated)
l.setAltitude(altitude);
}
public Calibration getCalibration()
{
return Calibration.UNCALIBRATED;
}
private void handleMessage(String m) {
/**
* Hi Kevin, So here's the output from the bluetooth barometer:
* printf("V%06ld A%07ld P%06ld B%03ld\r\n",Vspd,Alt,Pressure,**Battery)
*
* Vspd=CM/S and looks like 000005 or -00005 Alt=CM and looks like 0038002
* which would be 380.02 Meters Pressure=Pa and looks like 096840
* Battery=Volts and looks like 032 which would be 3.2 volts
*
* So the whole output looks like: V000005 A0038002 P096840 B032
*/
vspd = Integer.parseInt(m.substring(1, 1 + 6)) / 100.0f; // avoid using
// split,
// because it generates
// lots of allocs
altitude = Integer.parseInt(m.substring(9, 9 + 7)) / 100.f;
// convert pressure from Pa to hPa
pressure = Integer.parseInt(m.substring(18, 18 + 6)) / 100.f;
batVoltage = Integer.parseInt(m.substring(26, 26 + 3)) / 10.0f;
// Tell the GUI/audio vario we have new state
setChanged();
notifyObservers(pressure);
}
/** The background thread that talks to device */
@Override
public void run() {
BluetoothSocket socket = null;
try {
// FIXME, add outer loop to reconnect if bluetooth device is rebooted
socket = device.createRfcommSocketToServiceRecord(uuid);
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
socket.connect();
// Read messages
BufferedReader reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
handleMessage(line);
reader.close();
socket.close();
socket = null;
} catch (IOException connectException) {
// close the socket and get out
try {
if (socket != null)
socket.close();
} catch (IOException closeException) {
// Ignore errors on close
}
}
}
}