I am developing an app that requires real time sensor data streaming from a wearable. I am using an LG G watch and the accelerometer and gyroscope sensors.
The sensor data and system current time are packaged as an array of 4 bytes for each axis of each sensor X 32 samples (hence 4 X 3 X COUNT.
This data is then sent through the message api.
Main question:
I have registered the sensors with fastest update rate (SENSOR_DELAY_FASTEST) and when I analyze the data samples I have captured with the mobile device, the time step is roughly 200ms per sample (roughly 5 HZ).
I am expecting much faster.
Am I limited by:
1) bluetooth 4.0 bandwith
2) LG G watch sensor (it's an Invensense and the bandwidth should be much higher)... or
3) the message API
Would DataApi allow me to stream data to the mobile device much faster?
Here is a section of my code:
static final int COUNT = 32;
static ByteBuffer AcceleratorBuffer = ByteBuffer.allocate((4 * 3 * COUNT) + (8 * 1 * COUNT));
static ByteBuffer GyroBuffer = ByteBuffer.allocate((4 * 3 * COUNT) + (8 * 1 * COUNT));
static int accelerator_cycle = 0;
static int gyro_cycle = 0;
static AcceleratorWearSensorListener mAcceleratorSensorEventListener = null;
static GyroWearSensorListener mGyroSensorEventListener = null;
static class GyroWearSensorListener implements SensorEventListener {
GoogleApiClient mGoogleApiClient;
public GyroWearSensorListener(GoogleApiClient mGoogleApiClient) {
this.mGoogleApiClient = mGoogleApiClient;
}
#Override
public void onSensorChanged(SensorEvent event) {
if (On) {
Float x = event.values[0];
Float y = event.values[1];
Float z = event.values[2];
Log.i(TAG, "Gyro x: " + x + " y: " + y + " z: " + z);
GyroBuffer.putFloat(x).putFloat(y).putFloat(z).putLong(System.currentTimeMillis()).array();
++gyro_cycle;
if (gyro_cycle % COUNT == 0) {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult nodes) {
Log.i(TAG, "Nodes size : " + nodes.getNodes().size());
for (Node node : nodes.getNodes()) {
if (node != null && mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
Wearable.MessageApi.sendMessage(
mGoogleApiClient, node.getId(), GYROSCOPE_DATA_PACKET, GyroBuffer.array())
.setResultCallback(
new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG, "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
} else {
Log.e(TAG, "send gyro data successfully ");
}
}
}
);
} else {
Log.e(TAG, "node null or client null or not connected. ");
}
}
}
});
GyroBuffer.clear();
gyro_cycle = 0;
}
}
}
Related
I'm transfering an image of 1 mb using the following code.
The image gets transferred successfully if a thread delay is implemented between each packets.
If the thread delay is not set all the packets are sent from BluetoothGattServer but the BluetoothGattCallback does not receive all the packets.
Can anyone guide in sending the packets without the thread delay
Implement thread between each packets
private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
byte[] CHARACTERS) {
boolean isComplete = false;
runOnUiThread(() -> {
tv_status.setText("Sending Data...!!");
startTime = SystemClock.uptimeMillis();
customHandler.postDelayed(updateTimerThread, 0);
});
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] packetNoByte;
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
final int remainingTime = time;
if (!hasDisconnected) {
this.runOnUiThread(new Runnable() {
#Override
public void run() {
mRelativeLayout.setVisibility(View.VISIBLE);
if (totalPackets != 0) {
showProgress(totalPackets, remainingTime);
}
}
});
} else {
runOnUiThread(() -> {
mProgressBar.setProgress(0);
tv_progress.setText(0 + "%");
tv_timer.setText("00:00:00");
tv_imageSize.setText("");
tv_status.setText("");
Toast.makeText(PeripheralRoleActivity.this, "Something went wrong, Please Try again", Toast.LENGTH_SHORT).show();
customHandler.removeCallbacks(updateTimerThread);
});
return;
}
int a;
int b;
/**
* #param THREAD_SLEEP_TIME_FOR_NOTIFICATION
* this delay is placed to give a small pause while sending the data packe
* */
try {
Thread.sleep(Constants.THREAD_SLEEP_TIME_FOR_NOTIFICATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
sentPacket = sentPacket + 1;
byte[] packetArray = Utils.getUtilsClass().toByteArray(sentPacket);
packetNoByte = Arrays.copyOf(packetArray, packetArray.length);
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
if(b-a ==0){
return;
}
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] last_packet =
new byte[packetNoByte.length + character_length];
System.arraycopy(packetNoByte, 0, last_packet,
0, packetNoByte.length);
System.arraycopy(sending_last_hex, 0, last_packet,
packetNoByte.length, sending_last_hex.length);
Log.d("Sending packets", Arrays.toString(last_packet));
// Set value for characteristic
characteristic.setValue(last_packet);
notifyCharacteristicChanged();
isComplete = true;
customHandler.removeCallbacks(updateTimerThread);
currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
Log.d("Collection", "End Time: " + currentDateTimeString);
Utils.getUtilsClass().sendNotification(getApplicationContext(), "Data Transfer", "Transfer Complete");
} else {
Log.i("", "CONTINUE PACKET ");
a = ((sending_continue_hex.length) * time);
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] sending_continue_packet =
new byte[packetNoByte.length + sending_continue_hex.length];
System.arraycopy(packetNoByte, 0, sending_continue_packet,
0, packetNoByte.length);
System.arraycopy(sending_continue_hex, 0, sending_continue_packet,
packetNoByte.length, sending_continue_hex.length);
Log.d("data transfer a", String.valueOf(a));
Log.d("data transfer b", String.valueOf(b));
Log.d("data trans bytes", String.valueOf(sending_continue_hex.length));
if(output == null){
output = new ByteArrayOutputStream();
}
try {
if {
characteristic.setValue(sending_continue_packet);
Log.d("Sending packets", Arrays.toString(sending_continue_packet));
notifyCharacteristicChanged();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d("Data byte", "times " + time);
if (isComplete) {
characteristic.setValue("Completed");
notifyCharacteristicChanged();
}
runOnUiThread(() -> tv_status.setText("Data sent!!"));
}
}
Updated Code
//the following function is used break the image byte [] into packets and store it in an arraylist
private void breakPackets(byte[] CHARACTERS) {
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
packetList = new ArrayList<>();
sendingPacket = 0;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
int a;
int b;
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
//packetList is an ArrayList<byte[]>
packetList.add(sending_last_hex);
startSendingPackets(sendingPacket);
} else {
a = (sending_continue_hex.length) * time;
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
packetList.add(sending_continue_hex);
}
Log.d("Data byte", "times " + time);
}
}
//the following function is used to set the byte[] from the arraylist to the characteristics and then notify the characteristics
private void startSendingPackets(int packet) {
isCommand = false;
mSampleCharacteristic.setValue(packetList.get(packet));
notifyCharacteristicChanged();
Log.i("packeting", "Sending ------------> " + packet);
}
/*************************************************/
#Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
//check if status is success
if (status == BluetoothGatt.GATT_SUCCESS) {
//if status is not successful isExecutable is false and the else loop is executed to resend the same packet that has failed
if (isExecutable) {
// Log.i("packeting", "Sent ------------> " + sendingPacket);
sendingPacket = sendingPacket + 1;
int size = packetList.size();
if (sendingPacket <= size-1) {
startSendingPackets(sendingPacket);
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
} else {
sendCommand("Completed");
}
} else {
startSendingPackets(sendingPacket);
isExecutable = true;
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
}
}else{
//if status is not successful
isExecutable = false;
Log.d(MainActivity.TAG, "Notification sent. fail Status: " + status );
}
}
As can be read in the documentation at https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback.html#onNotificationSent(android.bluetooth.BluetoothDevice,%20int):
When multiple notifications are to be sent, an application must wait
for this callback to be received before sending additional
notifications.
This means after you have called notifyCharacteristicChanged, you cannot call notifyCharacteristicChanged again until the callback onNotificationSent has been received. So you need to remove your for-loop and refactor your code to follow the API rules.
The reason for this is to get flow control. If you just push new packets faster than the BLE link's throughput, the internal buffers get full and packet loss will occur. That's why a delay might seem to work, but it's not a robust solution so that's why you should wait for the onNotificationSent callback since that means the BLE stack is ready to accept new packets.
Sorry for doing this, I really don't know where to post beacuse this is both android studio code and arduino code so I post i both site.
I want to create a project of using controlling pan-tilt(2 servos combine) using the accelerometer and gyroscope data in smartphone. Creating android app and sending the accelerometer and gryroscope data to arduino so that the pan-tilt can determine its location.
My problem is when i connect the android app to arduino, no data shown in serial monitor of arduino and the pan-tilt is not locating the smartphone.
Please help me to solve this problem.....
this MainActivity is class to identify the data of accelerometer and gyrscope.
MainActivity:
public class MainActivity extends AppCompatActivity implements SensorEventListener{
private final String TAG = "MainActivity";//log our activity
SensorManager sm;//define sensor manager
Sensor accelerometer, gyrometer;//define accelerometer
TextView xValue, yValue, zValue, xGyroValue, yGyroValue, zGyroValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
xValue = (TextView)findViewById(R.id.xValue);
yValue = (TextView)findViewById(R.id.yValue);
zValue = (TextView)findViewById(R.id.zValue);
xGyroValue = (TextView)findViewById(R.id.xGyroValue);
yGyroValue = (TextView)findViewById(R.id.yGyroValue);
zGyroValue = (TextView)findViewById(R.id.zGyroValue);
Log.d(TAG, "onCreate: Initializing Sensor Services");
sm =(SensorManager) getSystemService(Context.SENSOR_SERVICE);//permission to use the sensor
accelerometer = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (accelerometer != null) {
sm.registerListener(MainActivity.this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
Log.d(TAG, "onCreate: Registered accelerometer listerner");
}else {
xValue.setText("accelerometer is not supported");
yValue.setText("accelerometer is not supported");
zValue.setText("accelerometer is not supported");
}
gyrometer = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (gyrometer != null) {
sm.registerListener(MainActivity.this, gyrometer, SensorManager.SENSOR_DELAY_NORMAL);
Log.d(TAG, "onCreate: Registered gyrometer listerner");
}else {
xGyroValue.setText("gyrometer is not supported");
yGyroValue.setText("gyrometer is not supported");
zGyroValue.setText("gyrometer is not supported");
}
}
public void connect(View v){
DataSender dataSender = new DataSender();
dataSender.execute((String) xValue.getText(), toString());
dataSender.execute((String) yValue.getText(), toString());
dataSender.execute((String) zValue.getText(), toString());
dataSender.execute((String) xGyroValue.getText(), toString());
dataSender.execute((String) yGyroValue.getText(), toString());
dataSender.execute((String) zGyroValue.getText(), toString());
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
if(sensor.getType() == Sensor.TYPE_ACCELEROMETER){
Log.d(TAG, "onSensorChanged: X: " + sensorEvent.values[0] + "Y: " + sensorEvent.values[1] + "Z:" + sensorEvent.values[2]);
xValue.setText("xValue: " + sensorEvent.values[0]);
yValue.setText("yValue: " + sensorEvent.values[1]);
zValue.setText("zValue: " + sensorEvent.values[2]);
}else if (sensor.getType() == Sensor.TYPE_GYROSCOPE){
xGyroValue.setText("xGyroValue: " + sensorEvent.values[0]);
yGyroValue.setText("yGyroValue: " + sensorEvent.values[1]);
zGyroValue.setText("zGyroValue: " + sensorEvent.values[2]);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
this DataSender is class for sending the data to arduino.
DataSender:
public class DataSender extends AsyncTask<String, Void, Void> {
Socket s;
DataOutputStream dos;
PrintWriter pw;
#Override
protected Void doInBackground(String... voids) {
String data = voids[0];
try {
s = new Socket("192.168.1.100",80);
pw = new PrintWriter(s.getOutputStream());
pw.write(data);
pw.flush();
pw.close();
s.close();
} catch (IOException e){
e.printStackTrace();
}
return null;
}
}
this is code that are uploaded in wifi module(esp8266-01) that are connected in arduino.
Arduino code:
#include <SoftwareSerial.h>
#include <Servo.h>
#define DEBUG true
char tiltChannel=0, panChannel=1;
//These are the objects for each servo.
Servo servoTilt, servoPan;
//This is a character that will hold data from the Serial port.
char serialChar=0;
// Center servos
int tiltVal = 90;
int panVal =90;
//smaartphone value
String inText;
float value0, value1, value2;
SoftwareSerial esp8266(10, 11); // RX, TX
void setup() { // Open serial communications and wait for port to open:
servoTilt.attach(2); //The Tilt servo is attached to pin 2.
servoPan.attach(3); //The Pan servo is attached to pin 3.
servoTilt.write(90); //Initially put the servos both
servoPan.write(90); //at 90 degress.
Serial.begin(9600); //for monitoring purposes
esp8266.begin(9600);
//sendCommand("AT+CIFS+RST\r\n", 2000, DEBUG); // reset module
sendCommand("AT+IPR=9600\r\n", 1000, DEBUG);
sendCommand("AT+CWMODE=1\r\n", 1000, DEBUG); // configure as access point
sendCommand("AT+CWJAP=\"EceConnect\",\"1234\"\r\n", 3000, DEBUG); //connec to
a network with name EceConnect with password 1234
delay(1000);
sendCommand("AT+CIFSR\r\n", 1000, DEBUG); // get ip address
sendCommand("AT+CIPSTA=\"192.168.1.100\"\r\n", 1000, DEBUG);
sendCommand("AT+CIPMUX=1\r\n", 1000, DEBUG); // configure for multiple
connections
sendCommand("AT+CIPSERVER=1,80\r\n", 1000, DEBUG); // turn on server on port
80
Serial.println("Server Ready");
Serial.println("Android Sensor Type No: ");
Serial.println("1- ACCELEROMETER (m/s^2 - X,Y,Z)");
Serial.println("2- GYROSCOPE (rad/s - X,Y,Z)");
Serial.flush();
}
void loop() { // run over and over
Serial.flush();
int inCommand = 0;
int sensorType = 0;
unsigned long logCount = 0L;
if (Serial.available() < 1) return; // if serial empty, return to loop().
char getChar = ' ';
if (esp8266.available()) {
if (esp8266.find("+IPD,0,")) {
delay(10);
esp8266.find(":");
delay(10);
char letter = esp8266.read();
Serial.print(letter); //for monitoring purposes
//Gets the value/char from android app
}
}
// parse incoming command start flag
if (getChar != serialChar) return; // if no command start flag, return to
loop().
// parse incoming pin# and value
sensorType = Serial.parseInt(); // read sensor typr
logCount = Serial.parseInt(); // read total logged sensor readings
value0 = Serial.parseFloat(); // 1st sensor value
value1 = Serial.parseFloat(); // 2rd sensor value if exists
value2 = Serial.parseFloat(); // 3rd sensor value if exists
// send smartphone readings to serial monitor/terminal
if (DEBUG) {
Serial.print("Sensor type: ");
Serial.println(sensorType);
Serial.print("Sensor log#: ");
Serial.println(logCount);
Serial.print("Val[0]: ");
Serial.println(value0);
Serial.print("Val[1]: ");
Serial.println(value1);
Serial.print("Val[2]: ");
Serial.println(value2);
Serial.println("-----------------------");
delay(10);
}
// Check sensor type. If not for Accelerometer (#1) then ignore readings
// sensorType 1 is the Accelerometer sensor
if (sensorType !=1) return;
panVal = value0; // value0 = X sensor reading
tiltVal = value1; // value1 = Y sensor reading
tiltVal = map(tiltVal, 10, -10, 0, 179); // Map Accelerometer Y value to
tilt servo angle.
servoTilt.write(tiltVal);
delay(10);
panVal = map(panVal, -10, 10, 0, 179); // Map Accelerometer X value to pan
servo angle.
servoPan.write(panVal);
delay(10);
}
String sendCommand(String command, const int timeout, boolean debug) {
String response = "";
esp8266.print(command); // send the read character to the esp8266
long int time = millis();
while ((time + timeout) > millis()) {
while (esp8266.available()) {
// The esp has data so display its output to the serial window
char c = esp8266.read(); // read the next character.
response += c;
}
}
if (debug) {
Serial.print(response);
}
return response;
}
I'm owning a Polar H10 device and I'm interested in the heart rate as well as RR-interval which I read out with the official bluetooth low energy API of Android. The Polar device sends every every second a package with the heart rate and the RR-interval. Now I have recognized that in every such package is a heart rate value but in some packages there are no RR-interval values (The value of the RR-interval is -1).
Why does this happen? Is my device broken or did I made a mistake in the implementation or does somebody else also face this issue?
Edit: Here is the code. In the method public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) I'm receiving changed values from the Polar Device. This method is triggered approximately every second. Then I parse the characteristic as follows:
public int[] parse(BluetoothGattCharacteristic characteristic) {
double heartRate = extractHeartRate(c);
Integer[] interval = extractBeatToBeatInterval(c);
int[] result = null;
if (interval != null) {
result = new int[interval.length + 1];
} else {
result = new int[2];
result[1] = -1;
}
result[0] = (int) heartRate;
if (interval != null) {
for (int i = 0; i < interval.length; i++) {
result[i+1] = interval[i];
}
}
return result;
}
private static double extractHeartRate(
BluetoothGattCharacteristic characteristic) {
int flag = characteristic.getProperties();
Log.d(TAG, "Heart rate flag: " + flag);
int format = -1;
// Heart rate bit number format
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
return heartRate;
}
private static Integer[] extractBeatToBeatInterval(
BluetoothGattCharacteristic characteristic) {
int flag = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
int format = -1;
int energy = -1;
int offset = 1; // This depends on hear rate value format and if there is energy data
int rr_count = 0;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
offset = 3;
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
offset = 2;
}
if ((flag & 0x08) != 0) {
// calories present
energy = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
Log.d(TAG, "Received energy: {}"+ energy);
}
if ((flag & 0x16) != 0){
// RR stuff.
Log.d(TAG, "RR stuff found at offset: "+ offset);
Log.d(TAG, "RR length: "+ (characteristic.getValue()).length);
rr_count = ((characteristic.getValue()).length - offset) / 2;
Log.d(TAG, "RR length: "+ (characteristic.getValue()).length);
Log.d(TAG, "rr_count: "+ rr_count);
if (rr_count > 0) {
Integer[] mRr_values = new Integer[rr_count];
for (int i = 0; i < rr_count; i++) {
mRr_values[i] = characteristic.getIntValue(
BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
Log.d(TAG, "Received RR: " + mRr_values[i]);
}
return mRr_values;
}
}
Log.d(TAG, "No RR data on this update: ");
return null;
}
The first element returned by the parse method is the heart rate and the second element is the RR-interval. It happens that sometimes the second element is -1 (i.e. no RR-interval detected).
There is nothing wrong with your Polar device or the software you posted.
The RR-interval measure may be missing from some transmitted packets and the if ((flag & 0x16) != 0) accounts for this case.
Suppose for example that your device send a heart measure every second and you have 50 beats/sec: there will be some intervals where the RR interval is not measured because there isnt a detected beat in that second (it is a simplified explanation, just to get the point).
I've been looking around and unfortunately the android ibeacon library has been deprecated, so I am attempting to do this native. I have implemented the BluetoothAdapter.LeScanCallback and the built in onLeScan() method that will fire when a device is picked up. I would like to read in that device's ProximityUUID, major and minor characteristics and identifier. I'm not sure how to get that information out of the Android object BluetoothDevice.
How do I extract that information (ProximityUUID, major, minor, & identifier characteristics) from the Android BluetoothDevice, or is there another way to do it?
Thanks!
you can refer this post to fully understand what those bytes means in LeScanCallback .
And this is my code to parse all information needed:
// an object with all information embedded from LeScanCallback data
public class ScannedBleDevice implements Serializable {
// public BluetoothDevice BLEDevice;
/**
* Returns the hardware address of this BluetoothDevice.
* <p>
* For example, "00:11:22:AA:BB:CC".
*
* #return Bluetooth hardware address as string
*/
public String MacAddress;
public String DeviceName;
public double RSSI;
public double Distance;
public byte[] CompanyId;
public byte[] IbeaconProximityUUID;
public byte[] Major;
public byte[] Minor;
public byte Tx;
public long ScannedTime;
}
// use this method to parse those bytes and turn to an object which defined proceeding.
// the uuidMatcher works as a UUID filter, put null if you want parse any BLE advertising data around.
private ScannedBleDevice ParseRawScanRecord(BluetoothDevice device,
int rssi, byte[] advertisedData, byte[] uuidMatcher) {
try {
ScannedBleDevice parsedObj = new ScannedBleDevice();
// parsedObj.BLEDevice = device;
parsedObj.DeviceName = device.getName();
parsedObj.MacAddress = device.getAddress();
parsedObj.RSSI = rssi;
List<UUID> uuids = new ArrayList<UUID>();
int skippedByteCount = advertisedData[0];
int magicStartIndex = skippedByteCount + 1;
int magicEndIndex = magicStartIndex
+ advertisedData[magicStartIndex] + 1;
ArrayList<Byte> magic = new ArrayList<Byte>();
for (int i = magicStartIndex; i < magicEndIndex; i++) {
magic.add(advertisedData[i]);
}
byte[] companyId = new byte[2];
companyId[0] = magic.get(2);
companyId[1] = magic.get(3);
parsedObj.CompanyId = companyId;
byte[] ibeaconProximityUUID = new byte[16];
for (int i = 0; i < 16; i++) {
ibeaconProximityUUID[i] = magic.get(i + 6);
}
if (uuidMatcher != null) {
if (ibeaconProximityUUID.length != uuidMatcher.length) {
Log.e(LOG_TAG,
"Scanned UUID: "
+ Util.BytesToHexString(
ibeaconProximityUUID, " ")
+ " filtered by UUID Matcher "
+ Util.BytesToHexString(uuidMatcher, " ")
+ " with length requirment.");
return null;
}
for (int i = 0; i < 16; i++) {
if (ibeaconProximityUUID[i] != uuidMatcher[i]) {
Log.e(LOG_TAG,
"Scanned UUID: "
+ Util.BytesToHexString(
ibeaconProximityUUID, " ")
+ " filtered by UUID Matcher "
+ Util.BytesToHexString(uuidMatcher,
" "));
return null;
}
}
}
parsedObj.IbeaconProximityUUID = ibeaconProximityUUID;
byte[] major = new byte[2];
major[0] = magic.get(22);
major[1] = magic.get(23);
parsedObj.Major = major;
byte[] minor = new byte[2];
minor[0] = magic.get(24);
minor[1] = magic.get(25);
parsedObj.Minor = minor;
byte tx = 0;
tx = magic.get(26);
parsedObj.Tx = tx;
parsedObj.ScannedTime = new Date().getTime();
return parsedObj;
} catch (Exception ex) {
Log.e(LOG_TAG, "skip one unknow format data...");
// Log.e(LOG_TAG,
// "Exception in ParseRawScanRecord with advertisedData: "
// + Util.BytesToHexString(advertisedData, " ")
// + ", detail: " + ex.getMessage());
return null;
}
}
Payloads of advertising packets should be parsed as a list of AD structures.
iBeacon is a kind of AD structures.
See "iBeacon as a kind of AD structures" for details. Also, see an answer to a similar question.
I am using a samsung galaxy ace3 to sample accelerations. getMinDelay() gives me 10 000 µs. When I log my accelerations with that period to a csv file, I get delays around the correct period with a random jitter which is sometimes huge (above 0.5 second).
Does someone know how to improve this?
Here are the delays between samples that I got.
public class MainActivity extends Activity implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mSensor;
private int cpt = 0;
private boolean start;
private List<String> records = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
public final File dir = new File("/mnt/extSdCard/test/");
public FileOutputStream afos = null;
public OutputStream stream = null;
public void startButton(View view) {
start = true;
cpt = 0;
mSensorManager.registerListener((SensorEventListener) this, mSensor, 10000); // 10000 µs -> 10 ms
openCsvFiles(dir, "data.csv");
stream = new BufferedOutputStream(afos);
}
public void stopButton(View view) {
for (String record: records) {
try {
stream.write(record.getBytes());
} catch (IOException e) {
}
}
try {
stream.flush();
stream.close();
afos.close();
start = false;
} catch (IOException e) {
Toast.makeText(MainActivity.this, "exception: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
Toast.makeText(MainActivity.this, "Done", Toast.LENGTH_SHORT).show();
}
#Override
public final void onSensorChanged(SensorEvent event) {
float ax, ay, az;
String axs, ays, azs, ts, line;
if (start) {
cpt++;
ax = event.values[0];
ay = event.values[1];
az = event.values[2];
ts = String.valueOf(event.timestamp);
long ns = android.os.SystemClock.elapsedRealtimeNanos();
line = ns+", "+ cpt+", "+ ts + "," + ax + "," + ay + "," + az + "\n";
records.add(line);
}
}
}
mSensorManager.registerListener((SensorEventListener) this, mSensor, 10000);
Instead of hard coded value 10000 use SensorManager.SENSOR_DELAY_FASTEST
mSensorManager.registerListener((SensorEventListener) this, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
The documentation for onSensorChanged says that you should not do much processing within the method because it blocks further samples coming in. It says that on this page:
http://developer.android.com/guide/topics/sensors/sensors_overview.html
Here, you are creating a string from the data and adding it to an ArrayList. It takes a certain amount of time to create the string, and usually that will be added quickly into the ArrayList, but there are occasions when the ArrayList will need to grow to accommodate the next object and on such occasions it takes a long time because it needs to allocate a new (larger) array internally and copy the references from the old array to the new one.
Consider adding the SensorEvent to a FIFO such as an ArrayDeque within the method and then processing these in another thread. It should improve the jitter.
I don't think the Android system provides any guarantees that the jitter will never be large, but this should help.