HTS profile rebuild to use BLE Common library

This commit is contained in:
Aleksander Nowakowski
2018-05-22 17:37:57 +02:00
parent 16f5832a31
commit 37df7ea57c
8 changed files with 130 additions and 142 deletions

View File

@@ -43,18 +43,19 @@ import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity;
/**
* HTSActivity is the main Health Thermometer activity. It implements {@link HTSManagerCallbacks} to receive callbacks from {@link HTSManager} class. The activity supports portrait and landscape
* HTSActivity is the main Health Thermometer activity. It implements {@link HTSManagerCallbacks}
* to receive callbacks from {@link HTSManager} class. The activity supports portrait and landscape
* orientations.
*/
public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBinder> {
public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.HTSBinder> {
@SuppressWarnings("unused")
private final String TAG = "HTSActivity";
private static final String VALUE = "value";
private static final DecimalFormat mFormattedTemp = new DecimalFormat("#0.00");
private TextView mHTSValue;
private TextView mHTSUnit;
private Double mValueC;
private TextView mTempValue;
private TextView mUnit;
private TextView mBatteryLevelView;
private Float mValueC;
@Override
protected void onCreateView(final Bundle savedInstanceState) {
@@ -76,7 +77,7 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(VALUE))
mValueC = savedInstanceState.getDouble(VALUE);
mValueC = savedInstanceState.getFloat(VALUE);
}
}
@@ -87,11 +88,12 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
}
private void setGUI() {
mHTSValue = findViewById(R.id.text_hts_value);
mHTSUnit = findViewById(R.id.text_hts_unit);
mTempValue = findViewById(R.id.text_hts_value);
mUnit = findViewById(R.id.text_hts_unit);
mBatteryLevelView = findViewById(R.id.battery);
if (mValueC != null)
mHTSValue.setText(String.valueOf(mValueC));
mTempValue.setText(String.valueOf(mValueC));
}
@Override
@@ -103,7 +105,8 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
@Override
protected void setDefaultUI() {
mValueC = null;
mHTSValue.setText(R.string.not_available_value);
mTempValue.setText(R.string.not_available_value);
mBatteryLevelView.setText(R.string.not_available);
setUnits();
}
@@ -114,21 +117,21 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
switch (unit) {
case SettingsFragment.SETTINGS_UNIT_C:
mHTSUnit.setText(R.string.hts_unit_celsius);
mUnit.setText(R.string.hts_unit_celsius);
break;
case SettingsFragment.SETTINGS_UNIT_F:
mHTSUnit.setText(R.string.hts_unit_fahrenheit);
mUnit.setText(R.string.hts_unit_fahrenheit);
break;
case SettingsFragment.SETTINGS_UNIT_K:
mHTSUnit.setText(R.string.hts_unit_kelvin);
mUnit.setText(R.string.hts_unit_kelvin);
break;
}
if (mValueC != null)
setHTSValueOnView(mValueC);
onTemperatureMeasurementReceived(mValueC);
}
@Override
protected void onServiceBound(final HTSService.RSCBinder binder) {
protected void onServiceBound(final HTSService.HTSBinder binder) {
// not used
}
@@ -184,14 +187,20 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
// this may notify user or show some views
}
private void setHTSValueOnView(double value) {
@Override
public void onDeviceDisconnected(final BluetoothDevice device) {
super.onDeviceDisconnected(device);
mBatteryLevelView.setText(R.string.not_available);
}
private void onTemperatureMeasurementReceived(float value) {
mValueC = value;
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final int unit = Integer.parseInt(preferences.getString(SettingsFragment.SETTINGS_UNIT, String.valueOf(SettingsFragment.SETTINGS_UNIT_DEFAULT)));
switch (unit) {
case SettingsFragment.SETTINGS_UNIT_F:
value = value * 1.8 + 32;
value = value * 1.8f + 32f;
break;
case SettingsFragment.SETTINGS_UNIT_K:
value += 273.15;
@@ -199,7 +208,11 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
case SettingsFragment.SETTINGS_UNIT_C:
break;
}
mHTSValue.setText(mFormattedTemp.format(value));
mTempValue.setText(getString(R.string.hts_value, value));
}
public void onBatteryLevelChanged(final int value) {
mBatteryLevelView.setText(getString(R.string.battery, value));
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -208,9 +221,13 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
final String action = intent.getAction();
if (HTSService.BROADCAST_HTS_MEASUREMENT.equals(action)) {
final double value = intent.getDoubleExtra(HTSService.EXTRA_TEMPERATURE, 0.0f);
final float value = intent.getFloatExtra(HTSService.EXTRA_TEMPERATURE, 0.0f);
// Update GUI
setHTSValueOnView(value);
onTemperatureMeasurementReceived(value);
} else if (HTSService.BROADCAST_BATTERY_LEVEL.equals(action)) {
final int batteryLevel = intent.getIntExtra(HTSService.EXTRA_BATTERY_LEVEL, 0);
// Update GUI
onBatteryLevelChanged(batteryLevel);
}
}
};
@@ -218,6 +235,7 @@ public class HTSActivity extends BleProfileServiceReadyActivity<HTSService.RSCBi
private static IntentFilter makeIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(HTSService.BROADCAST_HTS_MEASUREMENT);
intentFilter.addAction(HTSService.BROADCAST_BATTERY_LEVEL);
return intentFilter;
}
}

View File

@@ -21,27 +21,30 @@
*/
package no.nordicsemi.android.nrftoolbox.hts;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Calendar;
import java.util.UUID;
import no.nordicsemi.android.ble.BleManager;
import no.nordicsemi.android.ble.Request;
import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementDataCallback;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.log.LogContract;
import no.nordicsemi.android.nrftoolbox.battery.BatteryManager;
import no.nordicsemi.android.nrftoolbox.parser.TemperatureMeasurementParser;
import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
/**
* HTSManager class performs BluetoothGatt operations for connection, service discovery, enabling indication and reading characteristics. All operations required to connect to device with BLE HT
* Service and reading health thermometer values are performed here. HTSActivity implements HTSManagerCallbacks in order to receive callbacks of BluetoothGatt operations
* HTSManager class performs BluetoothGatt operations for connection, service discovery, enabling
* indication and reading characteristics. All operations required to connect to device with BLE HT
* Service and reading health thermometer values are performed here.
* HTSActivity implements HTSManagerCallbacks in order to receive callbacks of BluetoothGatt operations.
*/
public class HTSManager extends BleManager<HTSManagerCallbacks> {
public class HTSManager extends BatteryManager<HTSManagerCallbacks> {
private static final String TAG = "HTSManager";
/** Health Thermometer service UUID */
@@ -51,32 +54,39 @@ public class HTSManager extends BleManager<HTSManagerCallbacks> {
private BluetoothGattCharacteristic mHTCharacteristic;
private final static int HIDE_MSB_8BITS_OUT_OF_32BITS = 0x00FFFFFF;
private final static int HIDE_MSB_8BITS_OUT_OF_16BITS = 0x00FF;
private final static int SHIFT_LEFT_8BITS = 8;
private final static int SHIFT_LEFT_16BITS = 16;
private final static int GET_BIT24 = 0x00400000;
private final static int FIRST_BIT_MASK = 0x01;
public HTSManager(final Context context) {
HTSManager(final Context context) {
super(context);
}
@NonNull
@Override
protected BleManagerGattCallback getGattCallback() {
protected BatteryManagerGattCallback getGattCallback() {
return mGattCallback;
}
/**
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc.
*/
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
private final BatteryManagerGattCallback mGattCallback = new BatteryManagerGattCallback() {
@Override
protected void initialize() {
super.initialize();
setIndicationCallback(mHTCharacteristic)
.with(new TemperatureMeasurementDataCallback() {
@Override
public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
log(LogContract.Log.Level.APPLICATION, "\"" + TemperatureMeasurementParser.parse(data) + "\" received");
super.onDataReceived(device, data);
}
@Override
protected Deque<Request> initGatt(@NonNull final BluetoothGatt gatt) {
final LinkedList<Request> requests = new LinkedList<>();
requests.add(Request.newEnableIndicationsRequest(mHTCharacteristic));
return requests;
public void onTemperatureMeasurementReceived(final float temperature, final int unit,
@Nullable final Calendar calendar,
@Nullable final Integer type) {
mCallbacks.onTemperatureMeasurementReceived(temperature, unit, calendar, type);
}
});
enableIndications(mHTCharacteristic);
}
@Override
@@ -90,60 +100,8 @@ public class HTSManager extends BleManager<HTSManagerCallbacks> {
@Override
protected void onDeviceDisconnected() {
super.onDeviceDisconnected();
mHTCharacteristic = null;
}
@Override
public void onCharacteristicIndicated(@NonNull final BluetoothGatt gatt, @NonNull final BluetoothGattCharacteristic characteristic) {
log(LogContract.Log.Level.APPLICATION, "\"" + TemperatureMeasurementParser.parse(characteristic) + "\" received");
try {
final double tempValue = decodeTemperature(characteristic.getValue());
mCallbacks.onHTValueReceived(gatt.getDevice(), tempValue);
} catch (Exception e) {
DebugLogger.e(TAG, "Invalid temperature value", e);
}
}
};
/**
* This method decode temperature value received from Health Thermometer device First byte {0} of data is flag and first bit of flag shows unit information of temperature. if bit 0 has value 1
* then unit is Fahrenheit and Celsius otherwise Four bytes {1 to 4} after Flag bytes represent the temperature value in IEEE-11073 32-bit Float format
*/
private double decodeTemperature(byte[] data) throws Exception {
double temperatureValue;
byte flag = data[0];
byte exponential = data[4];
short firstOctet = convertNegativeByteToPositiveShort(data[1]);
short secondOctet = convertNegativeByteToPositiveShort(data[2]);
short thirdOctet = convertNegativeByteToPositiveShort(data[3]);
int mantissa = ((thirdOctet << SHIFT_LEFT_16BITS) | (secondOctet << SHIFT_LEFT_8BITS) | (firstOctet)) & HIDE_MSB_8BITS_OUT_OF_32BITS;
mantissa = getTwosComplimentOfNegativeMantissa(mantissa);
temperatureValue = (mantissa * Math.pow(10, exponential));
/*
* Conversion of temperature unit from Fahrenheit to Celsius if unit is in Fahrenheit
* Celsius = (Fahrenheit -32) 5/9
*/
if ((flag & FIRST_BIT_MASK) != 0) {
temperatureValue = (float) ((temperatureValue - 32) * (5 / 9.0));
}
return temperatureValue;
}
private short convertNegativeByteToPositiveShort(byte octet) {
if (octet < 0) {
return (short) (octet & HIDE_MSB_8BITS_OUT_OF_16BITS);
} else {
return octet;
}
}
private int getTwosComplimentOfNegativeMantissa(int mantissa) {
if ((mantissa & GET_BIT24) != 0) {
return ((((~mantissa) & HIDE_MSB_8BITS_OUT_OF_32BITS) + 1) * (-1));
} else {
return mantissa;
}
}
}

View File

@@ -21,22 +21,13 @@
*/
package no.nordicsemi.android.nrftoolbox.hts;
import android.bluetooth.BluetoothDevice;
import no.nordicsemi.android.ble.BleManagerCallbacks;
import no.nordicsemi.android.ble.common.profile.ht.TemperatureMeasurementCallback;
import no.nordicsemi.android.nrftoolbox.battery.BatteryManagerCallbacks;
/**
* Interface {@link HTSManagerCallbacks} must be implemented by {@link HTSActivity} in order to receive callbacks from {@link HTSManager}
* Interface {@link HTSManagerCallbacks} must be implemented by {@link HTSActivity} in order
* to receive callbacks from {@link HTSManager}.
*/
public interface HTSManagerCallbacks extends BleManagerCallbacks {
/**
* Called when Health Thermometer value has been received
*
* @param device the bluetooth device from which the value was obtained
* @param value
* the new value
*/
void onHTValueReceived(final BluetoothDevice device, double value);
interface HTSManagerCallbacks extends BatteryManagerCallbacks, TemperatureMeasurementCallback {
}

View File

@@ -30,20 +30,29 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import java.util.Calendar;
import no.nordicsemi.android.ble.BleManager;
import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementDataCallback;
import no.nordicsemi.android.log.Logger;
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
import no.nordicsemi.android.nrftoolbox.R;
import no.nordicsemi.android.nrftoolbox.ToolboxApplication;
import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
@SuppressWarnings("FieldCanBeLocal")
public class HTSService extends BleProfileService implements HTSManagerCallbacks {
public static final String BROADCAST_HTS_MEASUREMENT = "no.nordicsemi.android.nrftoolbox.hts.BROADCAST_HTS_MEASUREMENT";
public static final String EXTRA_TEMPERATURE = "no.nordicsemi.android.nrftoolbox.hts.EXTRA_TEMPERATURE";
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.hts.ACTION_DISCONNECT";
private final static int NOTIFICATION_ID = 267;
@@ -52,12 +61,12 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
private HTSManager mManager;
private final LocalBinder mBinder = new RSCBinder();
private final LocalBinder mBinder = new HTSBinder();
/**
* This local binder is an interface for the bonded activity to operate with the HTS sensor
*/
public class RSCBinder extends LocalBinder {
class HTSBinder extends LocalBinder {
// empty
}
@@ -102,10 +111,13 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
}
@Override
public void onHTValueReceived(final BluetoothDevice device, final double value) {
public void onTemperatureMeasurementReceived(final float temperature, final int unit,
@Nullable final Calendar calendar,
@Nullable final Integer type) {
final Intent broadcast = new Intent(BROADCAST_HTS_MEASUREMENT);
broadcast.putExtra(EXTRA_DEVICE, device);
broadcast.putExtra(EXTRA_TEMPERATURE, value);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_TEMPERATURE, TemperatureMeasurementDataCallback.toCelsius(temperature, unit));
// ignore the rest
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
if (!mBound) {
@@ -114,6 +126,14 @@ public class HTSService extends BleProfileService implements HTSManagerCallbacks
}
}
@Override
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}
/**
* Creates the notification
*

View File

@@ -21,37 +21,37 @@
*/
package no.nordicsemi.android.nrftoolbox.parser;
import android.bluetooth.BluetoothGattCharacteristic;
import java.util.Calendar;
import java.util.Locale;
import no.nordicsemi.android.ble.data.Data;
public class DateTimeParser {
/**
* Parses the date and time info.
*
* @param characteristic
* @param data
* @return time in human readable format
*/
public static String parse(final BluetoothGattCharacteristic characteristic) {
return parse(characteristic, 0);
public static String parse(final Data data) {
return parse(data, 0);
}
/**
* Parses the date and time info. This data has 7 bytes
*
* @param characteristic
* @param data
* @param offset
* offset to start reading the time
* @return time in human readable format
*/
/* package */static String parse(final BluetoothGattCharacteristic characteristic, final int offset) {
final int year = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
final int month = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 2);
final int day = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 3);
final int hours = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 4);
final int minutes = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 5);
final int seconds = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset + 6);
/* package */static String parse(final Data data, final int offset) {
final int year = data.getIntValue(Data.FORMAT_UINT16, offset);
final int month = data.getIntValue(Data.FORMAT_UINT8, offset + 2);
final int day = data.getIntValue(Data.FORMAT_UINT8, offset + 3);
final int hours = data.getIntValue(Data.FORMAT_UINT8, offset + 4);
final int minutes = data.getIntValue(Data.FORMAT_UINT8, offset + 5);
final int seconds = data.getIntValue(Data.FORMAT_UINT8, offset + 6);
final Calendar calendar = Calendar.getInstance();
calendar.set(year, month - 1, day, hours, minutes, seconds);

View File

@@ -21,18 +21,18 @@
*/
package no.nordicsemi.android.nrftoolbox.parser;
import android.bluetooth.BluetoothGattCharacteristic;
import java.util.Locale;
import no.nordicsemi.android.ble.data.Data;
public class TemperatureMeasurementParser {
private static final byte TEMPERATURE_UNIT_FLAG = 0x01; // 1 bit
private static final byte TIMESTAMP_FLAG = 0x02; // 1 bits
private static final byte TEMPERATURE_TYPE_FLAG = 0x04; // 1 bit
public static String parse(final BluetoothGattCharacteristic characteristic) {
public static String parse(final Data data) {
int offset = 0;
final int flags = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset++);
final int flags = data.getIntValue(Data.FORMAT_UINT8, offset++);
/*
* false Temperature is in Celsius degrees
@@ -52,18 +52,18 @@ public class TemperatureMeasurementParser {
*/
final boolean temperatureTypeIncluded = (flags & TEMPERATURE_TYPE_FLAG) > 0;
final float tempValue = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, offset);
final float tempValue = data.getFloatValue(Data.FORMAT_FLOAT, offset);
offset += 4;
String dateTime = null;
if (timestampIncluded) {
dateTime = DateTimeParser.parse(characteristic, offset);
dateTime = DateTimeParser.parse(data, offset);
offset += 7;
}
String type = null;
if (temperatureTypeIncluded) {
type = TemperatureTypeParser.parse(characteristic, offset);
type = TemperatureTypeParser.parse(data, offset);
// offset++;
}

View File

@@ -21,16 +21,16 @@
*/
package no.nordicsemi.android.nrftoolbox.parser;
import android.bluetooth.BluetoothGattCharacteristic;
import no.nordicsemi.android.ble.data.Data;
public class TemperatureTypeParser {
public static String parse(final BluetoothGattCharacteristic characteristic) {
return parse(characteristic, 0);
public static String parse(final Data data) {
return parse(data, 0);
}
/* package */static String parse(final BluetoothGattCharacteristic characteristic, final int offset) {
final int type = characteristic.getValue()[offset];
/* package */static String parse(final Data data, final int offset) {
final int type = data.getValue()[offset];
switch (type) {
case 1:

View File

@@ -31,6 +31,7 @@
<string name="hts_unit_celsius">°C</string>
<string name="hts_unit_fahrenheit">°F</string>
<string name="hts_unit_kelvin">°K</string>
<string name="hts_value">%.2f</string>
<string name="hts_notification_action_disconnect">Disconnect</string>
<string name="hts_notification_connected_message">%s is connected.</string>