I have this bluetooth temperature sensor https://www.aliexpress.com/item/nRF51822-Bluetooth-4-0-BLE-SOC-Temperature-Atmospheric-Pressure-Acceleration-Sensor-Module-Gyroscope-Light-Sensor-MPU6050/32859423925.html?spm=a2g0s.9042311.0.0.e3534c4dT9GRz3 and I am trying to read temperature out of it.
I can connect to it, get services via
BluetoothGatt.getService(UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e")
and get characteristics via
BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString("6e400005-b5a3-f393-e0a9-e50e24dcca9e"));
result:
mReadCharacteristic = {BluetoothGattCharacteristic#5322}
mDescriptors = {ArrayList#5326} size = 1
mInstance = 20
mKeySize = 16
mPermissions = 0
mProperties = 16
mService = {BluetoothGattService#5295}
mUuid = {UUID#5327} "6e400005-b5a3-f393-e0a9-e50e24dcca9e"
mValue = null
mWriteType = 2
Then I call mBluetoothGatt.readCharacteristic(mReadCharacteristic)
and hope to get data via BluetoothGattCallback, but readCharacteristic always returns false
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
return false;
}
properties = 16 PROPERTY_READ = 5
What am I doing wrong ?
Properties is a bitmask which operations the characteristic supports. As shown at https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html, 16 means notify and not read, so the characteristic can not be read. Maybe you should try to register for notifications instead?
Related
I am a novice in android development, so please forgive if the descriptions are not totally correct.
I am using the Android BTLE example app as a template and am attempting to send simple hex values to a Microchip RN4020 module. I can successfully send data (specifically time data using the calendar function) and they appear on my PuTTY window. However the problem is that the data is being 'transmitted' from the application (on my Samsung j5) as an ASCII representation.... in other words, the Calendar function returns the minute as say 20 (20hex = 32 decimal), which is somehow ending up as ASCII 20 on my PuTTY terminal (i.e. 32 30).
I am sending the data as a Byte array, which as I understand it constrains the range of values to be -127 to +127. So to combat this I have attempted to break the value into upper and lower nibbles and send these as separate bytes.....same problem. The value appearing is the ASCII 2 0....(i.e. 32 30).
I have taken the liberty of posting below my code (apologies for the 'clunkiness' of it). I strongly suspect it is a really rudimentary error, but I cant find how to transmit the 'hex values' Please help
regards
J
public void onClickTest(View v){
if(mBluetoothLeService != null) {
Calendar calendar = Calendar.getInstance();
//Data_1[0] = (Integer.toHexString((byte)calendar.get(Calendar.SECOND) & 0xFF)); //Alarm Set
int temp = 0;
int temp1 = 0;
byte first = 0;
byte second = 0;
temp = calendar.get(Calendar.HOUR_OF_DAY);
temp1 = temp;
temp = temp & 0xf0;
temp = temp >> 4;
temp1 = temp1 & 0x0f;
first = (byte)temp;
second = (byte)temp1;
Data_1[0] = first;
Data_1[1] = second; //Alarm Set
//Data_1[1] = 0xff; //Update clock
// Data_1[2] = calendar.get(Calendar.SECOND);
// Data_1[2] = calendar.get(Calendar.SECOND);
// Data_1[3] = (char)calendar.get(Calendar.MINUTE);
// Data_1[4] = (char)calendar.get(Calendar.HOUR_OF_DAY);
// Data_1[5] = (byte)calendar.get(Calendar.DAY_OF_WEEK);
// Data_1[6] = (byte)calendar.get(Calendar.MONTH);
// Data_1[7] = (byte)calendar.get(Calendar.YEAR);
//myArray = (Arrays.toString(Data_1));
mBluetoothLeService.writeCustomCharacteristicString(Data_1);
This is the actual LE service bit
public void writeCustomCharacteristicString(byte[] value) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
/*check if the service is available on the device*/
BluetoothGattService mCustomService1 = mBluetoothGatt.getService(UUID.fromString("12345678-9012-3456-7890-1234567890FF"));
if(mCustomService1 == null){
Log.w(TAG, "Custom BLE Service not found");
return;
}
/*get the read characteristic from the service*/
BluetoothGattCharacteristic mWriteCharacteristic1 = mCustomService1.getCharacteristic(UUID.fromString("12345678-9012-3456-7890-123456789011"));
mWriteCharacteristic1.setValue(value);
if(mBluetoothGatt.writeCharacteristic(mWriteCharacteristic1) == false){
Log.w(TAG, "Failed to write characteristic");
}
}
I'm developing an Android application which opens a BLE connection between the Android device and a BLE pheripheral (a simple transmitter).
The peripheral is programmed to have multiple reading characterics which I found.
The problem shows up when I try to enable the notification.
The first always returns true, and than it starts to trigger my notify callback, the others always return a false value.
List<BluetoothGattDescriptor> descrittoriDellaChar = getListaDescrittoriDaCharact(charact);
Boolean status = null;
for (int i = 0; i < descrittoriDellaChar.size(); i++) {
BluetoothGattDescriptor TargetDescriptor = descrittoriDellaChar.get(i);
byte[] valore = TargetDescriptor.getValue();
if (valore != BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
getCharDiLettura().add(charact);
TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
boolean success=false;
while(success==false) {
success = gattGlobale.writeDescriptor(TargetDescriptor);
}
status = gattGlobale.setCharacteristicNotification(charact, true);
}
}
boolean prio= gattGlobale.requestConnectionPriority(gattGlobale.CONNECTION_PRIORITY_HIGH);
I was using the same method since I had just 1 characteristic to read, and now it doesn't work anymore.
Sending read and write requests one after one other synchronously does not work since android only allows one pending GATT operation at a time (that's the reason it returns false). You must somehow enqueue the work and continue sending the next request once the callback (onCharacteristicRead/onCharacteristicWrite/onDescriptorWrite) of the previous request arrives.
I want to get the value of the HRM of an "A&D UA-651BLE" device.
this is what's written in the datasheet of this device to get the HRM value:
Set the application to pairing mode to start scanning.
Start pairing of A&D BLE device following each instruction manual.
At pairing mode, the application should set time and date and any other device settings
to A&D BLE device. After successful pairing, A&D BLE device shows “End” on the screen.
Take a measurement and finish the measurement, then A&D BLE device start BLE
connection with advertising. The application starts scanning with suitable interval so that
the application catches the advertising of A&D BLE device as soon as it can.
At initial connection or pairing, the Application set “2” to CCCD (Client Characteristic
Configuration Descriptor) so that A&D BLE device sends a measurement data with
Indication.
After A&D device recognizes to be set “2” to CCCD and to be synchronized time and date
within 5 seconds after connected, send the data with Indication.
If the timeout set CCCD and time and date is expired, A&D BLE device will not send data
and store the data in memory. The stored data in A&D BLE device can send next
successful connection.
this is my service code:
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
and this is the method that read data:
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
Log.e("HRM value",stringBuilder.toString());
dataComposition.put(characteristic.getUuid().toString(),stringBuilder.toString());
intent.putExtra(EXTRA_DATA,dataComposition);
}
the problem is that this code doesn't return any data !!
There's an Android Open Source Project example that does precisely this, easiest option would be to clone the android-BluetoothLeGatt code, build and compare it to your own. If you can't spot the difference / issue simply deploy both app's and step through both sets of code. Having some known working code will also help to rule out the possibility that the HRM is not functioning properly.
Do you have and example , i try this with equal device and i cant obtain the information y try with
public String response() {
if (mConnected) {
mBluetoothLeService.readCharacteristic(characteristica);
byte response[] = characteristica.getValue();
String respuesta = ReadBytes(response);
mBluetoothLeService.disconnect();
return respuesta;
} else {
return null;
}
}
On sometime, BluetoothDevice.getName() return null. How can i fix it?
remoteDeviceName maybe null in following code. And i need distinguish my device and other devices by remoteDeviceName.
BluetoothAdapter.getDefaultAdapter().startLeScan(new LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi,
byte[] scanRecord) {
String remoteDeviceName = device.getName();
Log.d("Scanning", "scan device " + remoteDeviceName);
});
Finally, i found out the solution:
1.For device connected:
Read device name from gatt characteristic org.bluetooth.characteristic.gap.device_name of service org.bluetooth.service.generic_access.
2.For device no connected:
/**
* Get device name from ble advertised data
*/
private LeScanCallback mScanCb = new LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi,
byte[] scanRecord) {
final BleAdvertisedData badata = BleUtil.parseAdertisedData(scanRecord);
String deviceName = device.getName();
if( deviceName == null ){
deviceName = badata.getName();
}
}
////////////////////// Helper Classes: BleUtil and BleAdvertisedData ///////////////
final public class BleUtil {
private final static String TAG=BleUtil.class.getSimpleName();
public static BleAdvertisedData parseAdertisedData(byte[] advertisedData) {
List<UUID> uuids = new ArrayList<UUID>();
String name = null;
if( advertisedData == null ){
return new BleAdvertisedData(uuids, name);
}
ByteBuffer buffer = ByteBuffer.wrap(advertisedData).order(ByteOrder.LITTLE_ENDIAN);
while (buffer.remaining() > 2) {
byte length = buffer.get();
if (length == 0) break;
byte type = buffer.get();
switch (type) {
case 0x02: // Partial list of 16-bit UUIDs
case 0x03: // Complete list of 16-bit UUIDs
while (length >= 2) {
uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
length -= 2;
}
break;
case 0x06: // Partial list of 128-bit UUIDs
case 0x07: // Complete list of 128-bit UUIDs
while (length >= 16) {
long lsb = buffer.getLong();
long msb = buffer.getLong();
uuids.add(new UUID(msb, lsb));
length -= 16;
}
break;
case 0x09:
byte[] nameBytes = new byte[length-1];
buffer.get(nameBytes);
try {
name = new String(nameBytes, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
break;
default:
buffer.position(buffer.position() + length - 1);
break;
}
}
return new BleAdvertisedData(uuids, name);
}
}
public class BleAdvertisedData {
private List<UUID> mUuids;
private String mName;
public BleAdvertisedData(List<UUID> uuids, String name){
mUuids = uuids;
mName = name;
}
public List<UUID> getUuids(){
return mUuids;
}
public String getName(){
return mName;
}
}
BluetoothDevice.getName() may return null if the name could not be determined. This could be due to any number of factors. Regardless, the name is the friendly name of the device, and shouldn't be used to distinguish it from other devices. Instead, use the hardware address through getAddress().
I know this is old but this more spec-oriented answer may help answer some cases.
In Bluetooth Low Energy, advertisement and scan-response data is only required to have the Bluetooth Address. Advertisement data is how a client BTLE endpoint discovers a service device. A client can request a scan response and get more data. The device name is optional in this data. However, the BTLE spec requires that all Bluetooth Low Energy endpoints support the Generic Access service which is required to support the Device Name characteristic. Unfortunately, to read that characteristic the Android must first connect and do service discovery. If the advertisement/scan response does not provide the information, I do not believe Android connects to the device to get the name. At least I have never seen any indication of connecting without the app specifically requesting a connection. This is not what you want to be required to do if you want to make a decision to connect.
Fortunately, most BTLE devices I have worked with do provide their name in the advertisement or scan response.
Another possibility is that the device may place the name in the scan response part of the advertisement. Depending upon how one has set up Android's BTLE scanner, one might get only the advertisement data and not the scan response. In this case the name will not be found if the device puts it in the scan response. The default scanner settings, however, are such that a scan response must be received before the scan data is passed up to the app.
On Marshmallow, utilize ScanRecord.getDeviceName() to retrieve the local name embedded in the scan record.
BluetoothDevice.getName() is unreliable if the local name is included in a scan response, rather than in the immediate advertising packet.
#Override
public void onScanResult(int callbackType, ScanResult scanResult) {
super.onScanResult(callbackType, scanResult);
// Retrieve device name via ScanRecord.
String deviceName = scanResult.getScanRecord().getDeviceName();
}
I was trying to display name of my RN4020 Bluetooth module and faced the same issue. Found the problem in Microchip's forum:
If you enabled private service or MLDP, the maximum bytes of device
name is 6 bytes, due to the 31 byte advertisement payload limitation.
I had set the device name to 9 characters. Setting the name to 4 bytes fixed the issue.
If you recognize the UUID's of your custom services so you know its your device you can also connect to the device and read its name (if its longer than 6 bytes in my case). This was also suggested in Microchips forum.
http://www.microchip.com/forums/m846328.aspx
I've found that if you query for the device's name immediately after it's picked up at scanning it may return null. To get around this I poll a runnable every second or so on the UI thread a maximum of 3 times (So 3 seconds), and the name is usually resolved by then.
Note, in the snippet provided, the enclosing class implements Runnable, hence why I can pass this into View.postDelayed(Runnable action, long delayMillis)
private static final int MAX_NAME_CHECKS = 3;
private static final int NAME_CHECK_PERIOD = 1000;
int nameChecks;
#Override
public void run() {
resolveName();
}
/**
* Checks for the device name, for a maximum of {#link ViewHolder#MAX_NAME_CHECKS}
* as the name may not have been resolved at binding.
*/
private void resolveName() {
if (device != null) {
String name = device.getName();
boolean isEmptyName = TextUtils.isEmpty(name);
if (isEmptyName) deviceName.setText(R.string.unknown_device);
else deviceName.setText(name);
// Check later if device name is resolved
if (nameChecks++ < MAX_NAME_CHECKS && isEmptyName)
itemView.postDelayed(this, NAME_CHECK_PERIOD);
}
}
For someone hasn't found the solution.
Bluetooth advertisement package has maximum size is 31 bytes. So if your device has long name. It can be truncated.
See: https://devzone.nordicsemi.com/f/nordic-q-a/14/what-s-the-maximum-size-for-an-advertisement-package
If you do want to get correct name of bluetooth device (even long name), please don't use startLeScan(). Instead of it, using method startDiscovery().
Google said: "The discovery process usually involves an inquiry scan of about 12 seconds, followed by a page scan of each device found to retrieve its Bluetooth name.". I've tried and it works like a charm.
See: https://developer.android.com/guide/topics/connectivity/bluetooth
has anyone tried using HM-10 Bluetooth module?
I'm able to pair with it using an Android device and passing the pre-defined PIN. Based on the UART return, the pairing is successful (module returns OK+CONN - means a connection was established)
However, after a few seconds (2-3), the UART receives OK+LOST; means the connection was lost. Also, the LED starts blinking (normally, when a connection is active, it stays lit)
Is this normal behaviour for bluetooth in general or the HM-10 module.
This is the product's website: http://www.jnhuamao.cn/bluetooth.asp?ID=1
I'm not sure, but HM -10 don't support rfcom. It's mean that you must use GATT functionality for communication. Entity of BLE is usage of minimum data package as it possible, so BLE don't hold the connection all times and use something like statuses [attributes].
So, few code lines for example, how work with BLE:
1.
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(DEVICE_ADDR);
That's device initiation, the same like with simple bluetooth, where DEVICE_ADDR is the MAC of your BLE(how to find this address you can find in google or stack overflow, its trivial)
2.
BluetoothGattService mBluetoothGattService;
BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> gattServices = mBluetoothGatt.getServices();
for(BluetoothGattService gattService : gattServices) {
if("0000ffe0-0000-1000-8000-00805f9b34fb".equals(gattService.getUuid().toString()))
{
mBluetoothGattService = gattService;
}
}
} else {
Log.d(TAG, "onServicesDiscovered received: " + status);
}
}
};
So, what this code mean: if u can see from this part of code, i describe how GATT service find. This service needed for "attribute" communication. gattService.getUuid() has few uuids for communication(4 in my module), some of them used for RX, some for TX etc. "0000ffe0-0000-1000-8000-00805f9b34fb" that is one of uuid that use for communication thats why i check it.
The final part of code is message sending:
BluetoothGattCharacteristic gattCharacteristic = mBluetoothGattService.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
String msg = "HELLO BLE =)";
byte b = 0x00;
byte[] temp = msg.getBytes();
byte[] tx = new byte[temp.length + 1];
tx[0] = b;
for(int i = 0; i < temp.length; i++)
tx[i+1] = temp[i];
gattCharacteristic.setValue(tx);
mBluetoothGatt.writeCharacteristic(gattCharacteristic);
After sending message contain hold on and you can send another message or can close connection.
More info, you can find on https://developer.android.com/guide/topics/connectivity/bluetooth-le.html.
PS: MAC address of your module can find with ble scanner code or with AT cmd:
on my firmware AT+ADDR or AT+LADDR
About UUIDs usage: not sure, but in my case, i find it with next AT+UUID [Get/Set system SERVER_UUID] -> Response +UUID=0xFFE0, AT+CHAR [Get/Set system CHAR_UUID] - Response +CHAR=0xFFE1. Thats why i make conclusion that UUID which i must use fe "0000[ffe0/is 0xFFE0 from AT response]-0000-1000-8000-00805f9b34fb"