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;
}
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.
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;
}
}
}
Im trying to get UUID of ble device. I was following android developers guide and so far I can get only device name and rssi. Im trying to get Uuid of the device that comes to scanning method that looks like this:
public void onLeScan(final BluetoothDevice device, int rssi,byte[] scanRecord) {
ParcelUuid[] myUUid =device.getUuids();
for(ParcelUuid a :myUUid){
Log.d("UUID",a.getUuid().toString());
}
String s = new String(scanRecord);
int len = scanRecord.length;
String scanRecords =new String(scanRecord) ;
deviceMap.put(device.getName().toString(), rssi);
Message msg = MainActivity.myHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putCharSequence("dev_name", device.getName().toString());
bundle.putCharSequence("rssi", Integer.toString(rssi));
msg.setData(bundle);
MainActivity.myHandler.sendMessage(msg);
}
this returns - btif_gattc_upstreams_evt: Event 4096
If you want to get UUID / any other data e.g. Manufacturer Data out of scanRec[] bytes after BLE Scan, you first need to understand the data format of those Advertisement Data packet.
Came from Bluetooth.org:
Too much theory, want to see some code snippet? This function below would straight forward print parsed raw data bytes. Now, you need to know each type code to know what data packet refers to what information. e.g. Type : 0x09, refers to BLE Device Name, Type : 0x07, refers to UUID.
public void printScanRecord (byte[] scanRecord) {
// Simply print all raw bytes
try {
String decodedRecord = new String(scanRecord,"UTF-8");
Log.d("DEBUG","decoded String : " + ByteArrayToString(scanRecord));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// Parse data bytes into individual records
List<AdRecord> records = AdRecord.parseScanRecord(scanRecord);
// Print individual records
if (records.size() == 0) {
Log.i("DEBUG", "Scan Record Empty");
} else {
Log.i("DEBUG", "Scan Record: " + TextUtils.join(",", records));
}
}
public static String ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.length * 2);
for (byte b : ba)
hex.append(b + " ");
return hex.toString();
}
public static class AdRecord {
public AdRecord(int length, int type, byte[] data) {
String decodedRecord = "";
try {
decodedRecord = new String(data,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Log.d("DEBUG", "Length: " + length + " Type : " + type + " Data : " + ByteArrayToString(data));
}
// ...
public static List<AdRecord> parseScanRecord(byte[] scanRecord) {
List<AdRecord> records = new ArrayList<AdRecord>();
int index = 0;
while (index < scanRecord.length) {
int length = scanRecord[index++];
//Done once we run out of records
if (length == 0) break;
int type = scanRecord[index];
//Done if our record isn't a valid type
if (type == 0) break;
byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length);
records.add(new AdRecord(length, type, data));
//Advance
index += length;
}
return records;
}
// ...
}
After this parsing, those data bytes would make more sense, and you can figure out next level of decoding.
As mentioned in comments, a BLE device doesn't really have a specific UUID (but rather many for included services). However, some schemes such as iBeacon encode a unique identifier in a manufacturer-specific data record in an advertising packet.
Here's a quite inefficient but conceptually simple way to convert the entire scanRecord to a hex string representation for debug printing:
String msg = "payload = ";
for (byte b : scanRecord)
msg += String.format("%02x ", b);
Note that this will include both the actual advertising packet and a number of meaningless trailing bytes, which should be ignored after parsing the structure (length field) contained in the advertising packet itself.
I had this same issue while developing my ble app, but after reading documentation on the following link: https://developer.android.com/reference/android/bluetooth/le/ScanResult.html
the important classes as far as UUID (Depending on the API you are developing for) is concerned are:
AdvertiseData
AdvertiseData.Builder
ScanRecord
ScanResult
after reading through documentation for these classes, this is the code I wrote to get UUID for any device being scanned:
//For API < 21:
private BluetoothAdapter.LeScanCallback scanCallBackLe =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, final byte[] scanRecord) {
final int RSSI = rssi;
if (RSSI >= signalThreshold){
scanHandler.post(new Runnable() {
#Override
public void run() {
AdvertiseData data = new AdvertiseData.Builder()
.addServiceUuid(ParcelUuid
.fromString(UUID
.nameUUIDFromBytes(scanRecord).toString())).build();
scannerActivity.addDevice(device, RSSI, getUUID(data));
}
});
}
}
};
//For APIs less than 21, Returns Device UUID
public String getUUID(AdvertiseData data){
List<ParcelUuid> UUIDs = data.getServiceUuids();
//ToastMakers.message(scannerActivity.getApplicationContext(), UUIDs.toString());
String UUIDx = UUIDs.get(0).getUuid().toString();
Log.e("UUID", " as list ->" + UUIDx);
return UUIDx;
}
For API's > 21:
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, final ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
final int RSSI = result.getRssi();
if (RSSI>=signalThreshold) {
scanHandler.post(
new Runnable() {
#Override
public void run() {
BluetoothDevice device = result.getDevice();
scannerActivity.addDevice(device, result.getRssi(), getUUID(result));
}
});
}
} ...}
//For APIs greater than 21, Returns Device UUID
public String getUUID(ScanResult result){
String UUIDx = UUID
.nameUUIDFromBytes(result.getScanRecord().getBytes()).toString();
ToastMakers.message(scannerActivity.getApplicationContext(), UUIDx);
Log.e("UUID", " as String ->>" + UUIDx);
return UUIDx;
}
I was able to obtain a 128 bit UUID of any device using this code. :)
I found a java library to parse the advertising packet
https://github.com/TakahikoKawasaki/nv-bluetooth
You can use standard android BluetoothGattCharacteristic apis like getFloatValue(int formatType, int offset), getIntValue(int formatType, int offset), getStringValue(int offset)..refer very nice android developer site tutorial here
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
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, "Received heart rate: " + heartRate);
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
}
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.
Im trying to get UUID of ble device. I was following android developers guide and so far I can get only device name and rssi. Im trying to get Uuid of the device that comes to scanning method that looks like this:
public void onLeScan(final BluetoothDevice device, int rssi,byte[] scanRecord) {
ParcelUuid[] myUUid =device.getUuids();
for(ParcelUuid a :myUUid){
Log.d("UUID",a.getUuid().toString());
}
String s = new String(scanRecord);
int len = scanRecord.length;
String scanRecords =new String(scanRecord) ;
deviceMap.put(device.getName().toString(), rssi);
Message msg = MainActivity.myHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putCharSequence("dev_name", device.getName().toString());
bundle.putCharSequence("rssi", Integer.toString(rssi));
msg.setData(bundle);
MainActivity.myHandler.sendMessage(msg);
}
this returns - btif_gattc_upstreams_evt: Event 4096
If you want to get UUID / any other data e.g. Manufacturer Data out of scanRec[] bytes after BLE Scan, you first need to understand the data format of those Advertisement Data packet.
Came from Bluetooth.org:
Too much theory, want to see some code snippet? This function below would straight forward print parsed raw data bytes. Now, you need to know each type code to know what data packet refers to what information. e.g. Type : 0x09, refers to BLE Device Name, Type : 0x07, refers to UUID.
public void printScanRecord (byte[] scanRecord) {
// Simply print all raw bytes
try {
String decodedRecord = new String(scanRecord,"UTF-8");
Log.d("DEBUG","decoded String : " + ByteArrayToString(scanRecord));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// Parse data bytes into individual records
List<AdRecord> records = AdRecord.parseScanRecord(scanRecord);
// Print individual records
if (records.size() == 0) {
Log.i("DEBUG", "Scan Record Empty");
} else {
Log.i("DEBUG", "Scan Record: " + TextUtils.join(",", records));
}
}
public static String ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.length * 2);
for (byte b : ba)
hex.append(b + " ");
return hex.toString();
}
public static class AdRecord {
public AdRecord(int length, int type, byte[] data) {
String decodedRecord = "";
try {
decodedRecord = new String(data,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Log.d("DEBUG", "Length: " + length + " Type : " + type + " Data : " + ByteArrayToString(data));
}
// ...
public static List<AdRecord> parseScanRecord(byte[] scanRecord) {
List<AdRecord> records = new ArrayList<AdRecord>();
int index = 0;
while (index < scanRecord.length) {
int length = scanRecord[index++];
//Done once we run out of records
if (length == 0) break;
int type = scanRecord[index];
//Done if our record isn't a valid type
if (type == 0) break;
byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length);
records.add(new AdRecord(length, type, data));
//Advance
index += length;
}
return records;
}
// ...
}
After this parsing, those data bytes would make more sense, and you can figure out next level of decoding.
As mentioned in comments, a BLE device doesn't really have a specific UUID (but rather many for included services). However, some schemes such as iBeacon encode a unique identifier in a manufacturer-specific data record in an advertising packet.
Here's a quite inefficient but conceptually simple way to convert the entire scanRecord to a hex string representation for debug printing:
String msg = "payload = ";
for (byte b : scanRecord)
msg += String.format("%02x ", b);
Note that this will include both the actual advertising packet and a number of meaningless trailing bytes, which should be ignored after parsing the structure (length field) contained in the advertising packet itself.
I had this same issue while developing my ble app, but after reading documentation on the following link: https://developer.android.com/reference/android/bluetooth/le/ScanResult.html
the important classes as far as UUID (Depending on the API you are developing for) is concerned are:
AdvertiseData
AdvertiseData.Builder
ScanRecord
ScanResult
after reading through documentation for these classes, this is the code I wrote to get UUID for any device being scanned:
//For API < 21:
private BluetoothAdapter.LeScanCallback scanCallBackLe =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, final byte[] scanRecord) {
final int RSSI = rssi;
if (RSSI >= signalThreshold){
scanHandler.post(new Runnable() {
#Override
public void run() {
AdvertiseData data = new AdvertiseData.Builder()
.addServiceUuid(ParcelUuid
.fromString(UUID
.nameUUIDFromBytes(scanRecord).toString())).build();
scannerActivity.addDevice(device, RSSI, getUUID(data));
}
});
}
}
};
//For APIs less than 21, Returns Device UUID
public String getUUID(AdvertiseData data){
List<ParcelUuid> UUIDs = data.getServiceUuids();
//ToastMakers.message(scannerActivity.getApplicationContext(), UUIDs.toString());
String UUIDx = UUIDs.get(0).getUuid().toString();
Log.e("UUID", " as list ->" + UUIDx);
return UUIDx;
}
For API's > 21:
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, final ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
final int RSSI = result.getRssi();
if (RSSI>=signalThreshold) {
scanHandler.post(
new Runnable() {
#Override
public void run() {
BluetoothDevice device = result.getDevice();
scannerActivity.addDevice(device, result.getRssi(), getUUID(result));
}
});
}
} ...}
//For APIs greater than 21, Returns Device UUID
public String getUUID(ScanResult result){
String UUIDx = UUID
.nameUUIDFromBytes(result.getScanRecord().getBytes()).toString();
ToastMakers.message(scannerActivity.getApplicationContext(), UUIDx);
Log.e("UUID", " as String ->>" + UUIDx);
return UUIDx;
}
I was able to obtain a 128 bit UUID of any device using this code. :)
I found a java library to parse the advertising packet
https://github.com/TakahikoKawasaki/nv-bluetooth
You can use standard android BluetoothGattCharacteristic apis like getFloatValue(int formatType, int offset), getIntValue(int formatType, int offset), getStringValue(int offset)..refer very nice android developer site tutorial here
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
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, "Received heart rate: " + heartRate);
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
}