I'm working on a BLE sensor that is advertising manufacturer specific data. Is there any sample code that demonstrates how to receive an advertisement packet in Android and parse its payload?
This is what I was looking for:
The BLE scan API BluetoothAdapter.startLeScan(ScanCallback) requires a call back function for the scan results. the method needs to look like the following:
private BluetoothAdapter.LeScanCallback ScanCallback =
new BluetoothAdapter.LeScanCallback()onLeScan(final BluetoothDevice device,
int rssi,
final byte[] scanRecord)
{...}
And the scanRecord variable is a byte array which contains the Advertisement packet payload.
Per the BLE specification the structure of the payload is very simple as follows:
The packets can be up to 47 bytes in length and consist of:
1 byte preamble
4 byte access address
2-39 bytes advertising channelPDU
3 bytes CRC
For advertisement communication channels, the access address is always 0x8E89BED6.
The PDU in turn has its own header (2 bytes: size of the payload and its type – whether the device supports connections, etc.) and the actual payload (up to 37 bytes).
Finally, the first 6 bytes of the payload are the MAC address of the device, and the actual information can have up to 31 bytes.
the format of the actual information is as follows:
first byte is length of the data and second byte is type followed by the data.
This is a clever way to allow any application to skip entire data records if they don't care about the contents.
Here is the sample code to determine the contents of the Advertisement packet:
parseAdvertisementPacket(final byte[] scanRecord) {
byte[] advertisedData = Arrays.copyOf(scanRecord, scanRecord.length);
int offset = 0;
while (offset < (advertisedData.length - 2)) {
int len = advertisedData[offset++];
if (len == 0)
break;
int type = advertisedData[offset++];
switch (type) {
case 0x02: // Partial list of 16-bit UUIDs
case 0x03: // Complete list of 16-bit UUIDs
while (len > 1) {
int uuid16 = advertisedData[offset++] & 0xFF;
uuid16 |= (advertisedData[offset++] << 8);
len -= 2;
uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", uuid16)));
}
break;
case 0x06:// Partial list of 128-bit UUIDs
case 0x07:// Complete list of 128-bit UUIDs
// Loop through the advertised 128-bit UUID's.
while (len >= 16) {
try {
// Wrap the advertised bits and order them.
ByteBuffer buffer = ByteBuffer.wrap(advertisedData,
offset++, 16).order(ByteOrder.LITTLE_ENDIAN);
long mostSignificantBit = buffer.getLong();
long leastSignificantBit = buffer.getLong();
uuids.add(new UUID(leastSignificantBit,
mostSignificantBit));
} catch (IndexOutOfBoundsException e) {
// Defensive programming.
Log.e("BlueToothDeviceFilter.parseUUID", e.toString());
continue;
} finally {
// Move the offset to read the next uuid.
offset += 15;
len -= 16;
}
}
break;
case 0xFF: // Manufacturer Specific Data
Log.d(TAG, "Manufacturer Specific Data size:" + len +" bytes" );
while (len > 1) {
if(i < 32) {
MfgData[i++] = advertisedData[offset++];
}
len -= 1;
}
Log.d(TAG, "Manufacturer Specific Data saved." + MfgData.toString());
break;
default:
offset += (len - 1);
break;
}
}
thanks to
how-ibeacons-work
bluetooth org specs
mass for putting me on the right direction!
ADPayloadParser in nv-bluetooth parses the payload of an advertising packet and returns a list of AD structures. The AD structure format is described in "11 ADVERTISING AND SCAN RESPONSE DATA FORMAT" of "Bluetooth Core Specification 4.2".
The following code snippet is an implementation example of onLeScan method.
public void onLeScan(
BluetoothDevice device, int rssi, byte[] scanRecord)
{
// Parse the payload of the advertising packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the advertising packet.
for (ADStructure structure : structures)
{
if (structure instanceof IBeacon)
{
// iBeacon packet was found.
handleIBeacon((IBeacon)structure);
}
......
}
}
You can register a parser of your own for your manufacturer-specific format into ADPayloadParser. Refer to the following links for more information.
Blog: http://darutk-oboegaki.blogspot.jp/2015/03/ibeacon-as-kind-of-ad-structures.html
GitHub: https://github.com/TakahikoKawasaki/nv-bluetooth
JavaDoc: http://takahikokawasaki.github.io/nv-bluetooth/
Maven: http://search.maven.org/#search|ga|1|a%3A%22nv-bluetooth%22
edit 21.02.2016
The library i linked below seems to have been moved;
see https://github.com/AltBeacon/android-beacon-library
You can use Android iBeacon Library for a start.
There is a reference application which you can use for the basics and with simulated data.
~https://github.com/RadiusNetworks/ibeacon-reference-android~
Once you get it up and running you may wish to import the library and use it with your real device, there is also some example code on the site:
http://developer.radiusnetworks.com/ibeacon/android/samples.html
Related
I am trying to use the PN532 to read my NFC UID from phone(Samsung Galaxy S10), but i receive just 08 and another 3 digits of random values.I read that a value that start with 08 is a RID(Random ID). Is there any possible way to read just a unique value, or use the PN532 to read something that is unique from my phones NFC?
I want to use that value to compare it with a constant in my code and send an impulse to a relay to open a door.
This code is from da Adafruit_PN532 library.
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>
#define PN532_IRQ (2)
#define PN532_RESET (3) // Not connected by default on the NFC Shield
// Or use this line for a breakout or shield with an I2C connection:
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
void setup(void) {
Serial.begin(115200);
Serial.println("Hello!");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC);
// configure board to read RFID tags
nfc.SAMConfig();
Serial.println("Waiting for an ISO14443A Card ...");
}
void loop(void) {
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
// Display some basic information about the card
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: "); Serial.print(uidLength, DEC); Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
if (uidLength == 4) {
// We probably have a Mifare Classic card ...
uint32_t cardid = uid[0];
cardid <<= 8;
cardid |= uid[1];
cardid <<= 8;
cardid |= uid[2];
cardid <<= 8;
cardid |= uid[3];
Serial.print("Seems to be a Mifare Classic card #");
Serial.println(cardid);
}
delay(2000);
}
}
Do not use the NFC UID for any security purpose, as you can see a phone does give a random one for privacy purposes.
The NFC UID is designed only to help reading hardware deal with handling sending data to the right card when multiple different Tags are in range. There is no guarantee that the UID is actually Unique and that it cannot duplicated (even with Tag that are supposed to have it programmed at the factory you can buy clones from China where they can be programmed by the end user).
It is better to use cryptographic methods with data stored on a tag or emulated Tag if using a phone to provide uniqueness to use a Tag for anything with security implications.
We are developping an app that uses Bluetooth library to communicate with an Arduino in bluetooth via an HC-05 module. We made a dummy configuration to test the delay without any computation from eather the Arduino or the app and we have a huge delay of about 1 second between a request and an answer...
Protocol looks easy : Android send byte -2 and if byte received is -2, Arduino send -6, -9 and Android answer again and again.
Android Code :
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE: // if receive massage
byte[] readBuf = (byte[]) msg.obj;
for(int i=0;i < readBuf.length;i++)
{
if((int) readBuf[i] != 0) {
txtArduino.append(String.valueOf((int) readBuf[i]) + ", ");
}
}
byte[] msg = {-2};
mConnectedThread.writeByte(msg);
break;
}
};
};
Arduino Code :
const int receveidBuffLen = 8*4;
void setup() {
Serial.begin(115200);
}
void loop() {
if (Serial.available() > 0)
{
byte buff[receveidBuffLen];
Serial.readBytes(buff, receveidBuffLen);
for(int i=0; i < receveidBuffLen;i++)
{
if(buff[i] == (byte) -2) // 254
{
byte message[2] = {(byte) -6, (byte) -9};
Serial.write(message, 2);
Serial.flush();
}
}
}
delay(3);
}
Does anyone know where the delay comes from?
We changed the HC05 baudrate (from 9600 to 115 200) : nothing happened. We changed HC05 with another : nothing happened. We used the Blue2Serial library (Bluetooth as SPP) before and delay was the same... We used another controler (ESP8266) and delay still was 1 second...
Looks like this string is an issue:
Serial.readBytes(buff, receveidBuffLen);
Where receveidBuffLen is 32.
Although you get single byte at a time, you're trying to read 32 of them. Of course, if there are no more bytes, the code will be stuck until timeout.
Furthermore, after bytes is read, you never check how many bytes were actually read, but do scan whole the array from bottom to top:
for(int i=0; i < receveidBuffLen;i++)
instead, you have to do something like this:
int bytesAvailable = Serial.available();
if (bytesAvailable > 0)
{
byte buff[receveidBuffLen];
int bytesToRead = (bytesAvailable < receveidBuffLen) ? bytesAvailable : receveidBuffLen;
// Read no more than the buffer size, but not more than available
int bytesActuallyRead = Serial.readBytes(buff, bytesToRead);
for(int i=0; i < bytesActuallyRead;i++)
...
There are a couple problems with the code that might cause delays:
delay function at end of loop - This will slow down the processing that the Ardunio can keep up with
Calling Serial.flush() - This will block the processing loop() until the internal TX serial buffer is empty. That means the Arduino is blocked and new RX data can pile up, slowing the response time.
Calling Serial.readBytes() - You should focus on the smallest unit of data and process that each loop() iteration. If you are trying to deal with multiple message per loop, that will slow now the loop time causing a delay.
You can try to implement a SerialEvent pattern on the Arduino. We will only read one byte at a time from the serial buffer, keeping the processing that the loop() function has todo to a bare minimum. If we receive the -2 byte we will mark a flag. If the flag is marked the loop() function will call the Serial.write() function but will not block for the data to transmit. Here is a quick example.
bool sendMessage = false;
byte message[2] = {(byte) -6, (byte) -9};
void loop()
{
if (sendMessage == true)
{
Serial.write(message, 2);
sendMessage = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the hardware serial RX. This
routine is run between each time loop() runs, so using delay inside loop can
delay response. Multiple bytes of data may be available.
*/
void serialEvent()
{
while (Serial.available())
{
// get the new byte:
byte inChar = ((byte) Serial.read());
if (inChar == ((byte) -2))
{
sendMessage = true;
}
}
}
We just find some solutions by ourselves and want to share them :
Initial situation : 1050 ms for an answer. Alls solutions are independent and done with the initial situation.
Remove Serial.flush() : 1022 ms.
Add a simple Serial.setTimeout(100) in Arduino Code : 135 ms. (Oh man!)
Add a simple timeout to inputStream of 100ms in Android : 95 ms.
Which solution is the best, we can't say but it works now...
I am trying to send more than 33 bytes using simple loops, Is anybody has idea how to send more than 20 bytes data over android ble.
if(!mConnected) return;
for (int i = 0; i<str.length;i++) {
if(str[i] == str[str.length -1]){
val = str[i]+"\n";
}else {
val = str[i] + "_";
}
System.out.println(val);
mBluetoothLeService.WriteValue(val);
}
Sending more than 20 bytes via BLE is easily achievable by splitting your data into 20 byte packets and implementing a short delay (i.e. using sleep()) between sending each packet.
Here's a short snippet of code from a project I'm working on that takes data in the form of byte[] and splits it into an array of the same, ( byte[][] ), in 20 byte chunks, and then sends it to another method that transmits each packet one by one.
int chunksize = 20;
byte[][] packets = new byte[packetsToSend][chunksize];
int packetsToSend = (int) Math.ceil( byteCount / chunksize);
for(int i = 0; i < packets.length; i++) {
packets[i] = Arrays.copyOfRange(source,start, start + chunksize);
start += chunksize;
}
sendSplitPackets(packets);
Here are two other very good explanations of how to achieve this:
(Stackoverflow) Android: Sending data >20 bytes by BLE
(Nordic Semi) Dealing Large Data Packets Through BLE
You can send more than 20 bytes of data without breaking into chunks and including a delay. Every characteristics you are trying to write has an MTU value assigned. It's number of bytes you can write in one time.
During the connection MTU values are exchanged and you can write those many bytes at a time. You can increase the mtu value on the server side (Max 512 bytes) and send that much bytes in one go.
For Android, you might want to request mtu manually after connecting with the server using
requestMtu(int mtu)
This is return true or false based on the mtu value you send. It will give a callback to onMtuChanged where Android device and server negotiate the maximum possible MTU value.
onMtuChanged (BluetoothGatt gatt, int mtu, int status)
and you can set MTU value in this function and can send more than 20 bytes in one go.
Some embedded bluetooth LE implementations limit the size of a characteristic to be 20 bytes. I know that the Laird BL600 series does this. This is limitation of the Laird module, even though the BLE spec calls for the max length to be longer. Other embedded BLE solutions have similar limits. I suspect this is the limitation that you are encountering.
Instead of using sleep for every chunk, i just found a better and efficient way for my application to send more than 20 bit data.
The packets will be send after onCharacteristicWrite() triggered. i just found out this method will be triggered automatically after peripheral device (BluetoothGattServer) sends a sendResponse() method.
firstly we have to transform the packet data into chunk with this function:
public void sendData(byte [] data){
int chunksize = 20; //20 byte chunk
packetSize = (int) Math.ceil( data.length / (double)chunksize); //make this variable public so we can access it on the other function
//this is use as header, so peripheral device know ho much packet will be received.
characteristicData.setValue(packetSize.toString().getBytes());
mGatt.writeCharacteristic(characteristicData);
mGatt.executeReliableWrite();
packets = new byte[packetSize][chunksize];
packetInteration =0;
Integer start = 0;
for(int i = 0; i < packets.length; i++) {
int end = start+chunksize;
if(end>data.length){end = data.length;}
packets[i] = Arrays.copyOfRange(data,start, end);
start += chunksize;
}
after our data ready, so i put my iteration on this function:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if(packetInteration<packetSize){
characteristicData.setValue(packets[packetInteration]);
mGatt.writeCharacteristic(characteristicData);
packetInteration++;
}
}
I want to only scan BLE beacons with a specific UUID in my Android code. Even though I can add filter for specific MAC addresses, I cannot make it work with UUIDs. onScanResult function is never called. Why could that be? I'm using API 21 and I'm not getting any errors for the project.
final String tagUUID = "01122334-4556-6778-899a-abbccddeeff0";
//does not work
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(UUID.fromString(tagUUID))).build();
//works
ScanFilter filter = new ScanFilter.Builder().setDeviceAddress(tagMAC).build();
I'm the author of the blog post mentioned above. Here's how to fix your issue for Android 21+.
// Empty data
byte[] manData = new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
// Data Mask
byte[] mask = new byte[]{0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
// Copy UUID into data array and remove all "-"
System.arraycopy(hexStringToByteArray("YOUR_UUID_TO_FILTER".replace("-","")), 0, manData, 2, 16);
// Add data array to filters
ScanFilter filter = new ScanFilter.Builder().setManufacturerData(76, manData, mask).build());
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
The issue here is that you can add UUID filtering but its not exactly straight forward
It is possible that the filtering looks for the service UUID in the "Service UUID" AD Type Advertising Structure. Which actually makes sense and that's how it should work.
For beacons, the UUID you are trying to find is actually located in the "Manufacturer Specific Data" AD Type structure. And nobody cares about looking for Service UUIDs there.
I believe that the service UUID filtering is only meant to filter for UUIDs of services in the GATT Database; those UUIDs would be located as I explained in the first paragraph.
That UUID in beacons is not a service UUID per se. It is rather a beacon identifier with an UUID format.
So I am facing a problem from a while now . Any suggestion would be good.
First I used my code to receive data from arduino , then I used the bluetoothChat and changed the uuid , I can pair , everything is good , but if I send an entire string from arduino to android I get only parts of that string.
If I use bluetooth terminal from google play everything is ok, and on the description it says it is made from the bluetooth Chat sample .
Code Arduino
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 9); //RX,TX
long int i = 0;
void setup(){
mySerial.begin(9600);
}
void loop(){
mySerial.print("This is a message n. ");
mySerial.println(i);
i++;
delay(100);
}
Android code : Bluetooth Chat Sample
Exemple of message received on Android:
Message to be sent!
So first messages I think are waiting while the module is paired .
because every time I get .
is is a message n. 466
This is a message n.467
.
. ( here I get correct messages )
.
This is a message n.470
message n. 495
.
.
and after the first messages I get messages like
ssage n.534
t
essage n.m
essage n.
535
( I neved again get an entire message )
Handler :
h = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE: // if receive massage
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1); // create string from bytes array
sb.append(strIncom); // append string
int endOfLineIndex = sb.indexOf("\r\n"); // determine the end-of-line
if (endOfLineIndex > 0) { // if end-of-line,
String sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length()); // and clear
Log.d("Arduino", "Mesaj:"+ sbprint.toString());
}
Log.d("Arduino", "...Mesaj:"+ sb.toString() + " Byte:" + msg.arg1 + "...");
break;
}
};
};
Listener to InputStream
public void run() {
byte[] buffer = new byte[256]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer); // Get number of bytes and message in "buffer"
h.obtainMessage(RECIEVE_MESSAGE, bytes, -1, buffer).sendToTarget(); // Send to message queue Handler
} catch (IOException e) {
break;
}
}
}
note you are using a software emulation of serial port, hence timing is not as good as it would be with a hardware UART.
It is likely one or both of the following two possible issues:
1) the start and stop bit are not properly timed, causing back to back bytes. Which occur when a string is set, rather then pecking in keys one at a time.
The solution would be to space out each key.
2) baud rates do not match with in tolerance. Either SLOWING DOWN or SPEEDING UP the baud rate on both the HC05 and Arduino will better match the timing.
I would also recommend ensuring your library is SoftwareSerial, states that it is NewSoftSerial. It has many issues fixed. It was implemented in to the Arduino IDE 1.0.+ core libraries, so if you have recent IDE you should have it.