I am attempting to communicate with an ELM327 OBDII Bluetooth dongle using the Android BluetoothChat example. I can connect to the device without any trouble, and messages from BluetoothChat to the ODBII device seem to be transmitted and received by the device correctly.
However, the response messages from the OBDII device are often split into several messages, scrambled, or missing characters.
For example, it takes three tries of the ati command to receive the full expected response:
Me: ati
OBDII: a
OBDII: 327
OBDII: 327
OBDII: 327 v1.5 >
Me: ati
OBDII: 1
OBDII: 1
OBDII: 1.5 >v
OBDII: 1.5 >
Me: ati
OBDII: ELM327 v1.5 >
Similarly, sending 010c should trigger a one line response containing three hex pairs. Instead, I usually (but not always) get results like the following:
Me: 010c
OBDII:
OBDII: 4
OBDII: 3C
OBDII: 3
OBDII: 3C C
OBDII: 3C
OBDII:
OBDII:
OBDII: >
I have tried several different baud rates and different OBDII protocols, but changes from the default settings only seem to make matters worse. Is there a problem with my response message handling? Why is the response message splitting? The Bluetooth dongle works properly with available apps such as Torque, so I don't think the device is malfunctioning.
The code that I'm using is almost identical to the BluetoothChat project (source here). I have only modified the UUID for my Bluetooth device and added a carriage return to the outgoing message (as per this StackOverflow question).
Change 1 (in BluetoothChatService.java):
// Unique UUID for this application
private static final UUID MY_UUID_SECURE =
//UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final UUID MY_UUID_INSECURE =
//UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Change 2 (in BluetoothChat.java):
// The action listener for the EditText widget, to listen for the return key
private TextView.OnEditorActionListener mWriteListener =
new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
// If the action is a key-up event on the return key, send the message
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
String message = view.getText().toString();
//sendMessage(message);
sendMessage(message + "\r");
}
if(D) Log.i(TAG, "END onEditorAction");
return true;
}
};
ELM327 manual for reference
I found a solution to my message splitting problem in this BluetoothSPP project by user akexorcist on Github. The relevant function from class ConnectedThread is presented below:
public void run() {
byte[] buffer;
ArrayList<Integer> arr_byte = new ArrayList<Integer>();
// Keep listening to the InputStream while connected
while (true) {
try {
int data = mmInStream.read();
if(data == 0x0A) {
} else if(data == 0x0D) {
buffer = new byte[arr_byte.size()];
for(int i = 0 ; i < arr_byte.size() ; i++) {
buffer[i] = arr_byte.get(i).byteValue();
}
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothState.MESSAGE_READ
, buffer.length, -1, buffer).sendToTarget();
arr_byte = new ArrayList<Integer>();
} else {
arr_byte.add(data);
}
} catch (IOException e) {
connectionLost();
// Start the service over to restart listening mode
BluetoothService.this.start(BluetoothService.this.isAndroid);
break;
}
}
}
Apparently, though please correct me if I'm wrong, .read() cannot grab and maintain the format of whatever bytes appear on the stream without some assistance.
Related
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 want to transfer some stringdata from my Android-device to my Windows-Laptop via Bluetooth.
Using the codesample for bluetooth with winsock2 provided by Microsoft I was able to transfer data using the below code. Unfortunately I receive a byte order mark at the beginning of the string I send. Of course, I could simply remove the first four bytes, but that seems a little bit dirty to me. Is there any other option I could use?
C++-code for receiving (slightly modified for better readability -> no error handling no comment, etc.)
ClientSocket = accept(LocalSocket, NULL, NULL);
BOOL bContinue = TRUE;
pszDataBuffer = (char *)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
CXN_TRANSFER_DATA_LENGTH);
pszDataBufferIndex = pszDataBuffer;
uiTotalLengthReceived = 0;
while ( bContinue && (uiTotalLengthReceived < CXN_TRANSFER_DATA_LENGTH) ) {
iLengthReceived = recv(ClientSocket,
(char *)pszDataBufferIndex,
(CXN_TRANSFER_DATA_LENGTH - uiTotalLengthReceived),
0);
switch ( iLengthReceived ) {
case 0: // socket connection has been closed gracefully
bContinue = FALSE;
break;
case SOCKET_ERROR:
wprintf(L"=CRITICAL= | recv() call failed. WSAGetLastError=[%d]\n", WSAGetLastError());
bContinue = FALSE;
ulRetCode = CXN_ERROR;
break;
default:
pszDataBufferIndex += iLengthReceived;
uiTotalLengthReceived += iLengthReceived;
break;
}
}
if ( CXN_SUCCESS == ulRetCode ) {
pszDataBuffer[uiTotalLengthReceived] = '\0';
wprintf(L"*INFO* | Received following data string from remote device:\n%s\n", (wchar_t *)pszDataBuffer);
closesocket(ClientSocket);
ClientSocket = INVALID_SOCKET;
}
Android-code for sending:
OutputStream socketOutpuStream = socket.getOutputStream();
socketOutputStream.write(dataString.getBytes(Charsets.UTF_16));
Ok, I feel quite stupid right now. Working in homogeneous java environments for some years made me completely forget that java sets the byte order mark when calling getBytes() with a unicodecharset as parameter.
After changing dataString.getBytes(Charsets.UTF_16) to dataString.getBytes(StandardCharsets.UTF_16LE) (windows is little endian) on the android side everything works as expected.
I'm trying to communicate between Arduino Mega Adk (ADK 2011) and android device.
Something goes ok, but something goes completely wrong.
Transfer data from Android to Arduino via acc.read from Arduino side works fine.
But when i try to send some bytes from Arduino to Android - something strange happens.
First of all here is Arduino sketch:
#include <Max3421e_constants.h>
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>
#define COMMAND_LED 0x2
#define TARGET_PIN_18 0x12
#define TARGET_PIN_19 0x13
#define V_ON 0x1
#define V_OFF 0x0
#define PIN_18 18
#define PIN_19 19
#define INPUT_PIN 30
AndroidAccessory acc("Google, Inc.",
"DemoKit",
"Ololo device board",
"1.0",
"http://www.android.com",
"0000000012345678");
byte rcvmsg[3];
byte sndmsg[3];
int buttonState = 0;
void setup();
void loop();
void led_setup(){
pinMode(PIN_18, OUTPUT);
pinMode(PIN_19, OUTPUT);
pinMode(INPUT_PIN, INPUT);
}
void setup()
{
Serial.begin(115200);
Serial.print("\r\nStart");
led_setup();
acc.powerOn();
}
void loop()
{
if (acc.isConnected()) {
buttonState = digitalRead(INPUT_PIN);
if (buttonState == 1){
sndmsg[0] = 0x2;
sndmsg[1] = 0x1;
sndmsg[2] = 0x1;
int len = acc.write(sndmsg, 3);
digitalWrite(PIN_19, HIGH);
}
else {
//Nothing here for test
}
}
//usefull test for button
buttonState = digitalRead(INPUT_PIN);
if (buttonState == 1){
digitalWrite(PIN_19, HIGH);
}
else {
digitalWrite(PIN_19, LOW);
}
}
Ok. When acc.write() is executed it takes up to ~1 second to transfer data to android. And this time doesn't depend on number of bytes in sndmsg. Only if i execute acc.write(sndmsg,0) (sending 0 bytes) - everything goes fast.
That is a little bit disturbing. I've tried to change board to another one but have got the same result.
Any advices? may be that is a common bug, but there is no such much information in web.
UPD:
Wrote some very simple code, that only sends 3 bytes via acc.write.
here it is:
#include <Max3421e_constants.h>
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>
AndroidAccessory acc("Google, Inc.",
"DemoKit",
"Demokit board",
"1.0",
"http://www.android.com",
"0000000012345678");
byte msg[3];
unsigned long time;
void setup();
void loop();
void led_setup(){
}
void setup()
{
Serial.begin(115200);
Serial.print("\r\nStart");
acc.powerOn();
}
void loop()
{
if (acc.isConnected()) {
Serial.print("\r\nis Connected");
msg[0] = 0x1;
msg[1] = 0x1;
msg[2] = 0x1;
//Serial.print("\r\nSending");
time = millis();
Serial.print ("\r\nBefore write\r\n");
Serial.print (time);
acc.write(msg, 3);
time = millis();
Serial.print("\r\nAfter write: \r\n");
Serial.print (time);
//delay(500);
}
}
And it's debug output is:
is Connected
Before write
6983
After write:
10958
is Connected
Before write
10958
After write:
14491
is Connected
and so on. So on some reasons acc.write takes a lot of time and there is no data in the android app.
New UPD (19.01.2015):
Today i've performed some experiments. Here are results.
First, i've looked into AndroidAccessory.cpp and found write function:
int AndroidAccessory::write(void *buff, int len)
{
usb.outTransfer(1, out, len, (char *)buff);
return len;
}
Ok, then i looked into usb host shield library and found there usb.cpp with outTransfer fucntion, that returns error code if ouccured and 0x00 if everything is ok.
So i modified write function to return an error code instead of lenght, like this:
int AndroidAccessory::write(void *buff, int len)
{
byte rcode;
rcode = usb.outTransfer(1, out, len, (char *)buff);
return int(rcode);
}
and recived "4" as result.
According to MAX3421Econstants.h it is hrNAK (0x04) error code.
Any ideas? Looks like accessory does not recive NAKs from Android and write fails as a result.
Situation update:
Did some research. There is a hell of NAK's when accessory is connected. Here is dump from usb connector:
i found the solution. And it is very simple - i didn't setup communication with accessory correctly.
This is not an Arduino problem. Arduino works fine.
It is just how android interacts with android accessory.
So, results:
When AndroidAccessory is plugged to Android and Android haven't setup
communication with the accessory yet, Android OS will send a lot of
USB NAKs to the accessory and this is normal.
You must be careful
during setuping communication with the accessory. If you make some
mistakes, you can receive same situation: Probably possible to write
to the accessory, but accessory isn't possible to write to the
android.
If UsbManager opens accessory correctly, it stops sending
NAKs and starts recieve data from arduino.
It is a little bit strange for me, because it was really hard to found a problem: i have an application, written according to this manual: http://developer.android.com/guide/topics/connectivity/usb/accessory.html But, because i'm not very familiar with android, it seems that i've done some mistakes and receive strange behavior:
i was able to write to arduino
i was able to retrive information about arduino as android accessory
it was possible to ask for permissions
but when arduino tries to write to android, it recievs a lot of NAKs
So i decided to rewrite program in more simple way, just with one function and tried to do it in right way. And after some debugging it finally started to work as i want.
So thank everyone, who spend time for reading this.
Bonus: dump of normal packet, ended with EOP not NAK
UPD 26.01.2015:
I found problem and it was in my android code.
here is explanation:
Android developer's manual said that function, which set up communication with accessory must start it's own thread in which all communications with input and output streams are held. Smth like this:
private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
mFileDescriptor = mUsbManager.openAccessory(mAccessory);
if (mFileDescriptor != null) {
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}
I really messed this thing up. Forgot about creating new thread in openAccessory function and tried to do it in different place. And recieve a hell of NAK's. Then i've changed my code and add some debug-like run() function like this:
public void run() {
final byte[] buffer = new byte[16384];
int ret = 0;
int i = 0;
while (i<50) {
try {
ret = mInputStream.read(buffer);
i++;
Thread.sleep(500);
} catch (IOException e) {
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
And until this run method exists (while i < 50) android reads arduino correctly. And when thread ends up (i > 50) - Arduino starts to readout Error Code 4 (NAK) from Android.
That's all folks.
I made a simple test application for quick debugging. I send some bytes, print what I sent on the phones screen and print what I receive.
When I send WRONG commands I get the corresponding error codes in the two byte SW1SW2.
I can also call my own command and override SW1SW2 with my own values and I can get them.
Here's the problem: When I send a CORRECT command the transceive command fails with the informative exception "Transceive failed".
If I send the correct command, but override SW1SW2 to something other than 90 00 then I get the SW value I set, but NO response data. (likely because the card does not send ODATA when SW1SW2 <> 90 00)
So how come I'm so sure I sent correct commands? Well other than messing with my own test command I called the GetAppId command - which failed saying I have to define AppId in the card.
So I define it in the card, send the same command and transceive fails.
So I'm pretty sure the problem is that transceiving fails when there is ODATA, but I do not understand WHY or how to FIX it.. help please!
EDIT: My card is the 7.5 D contactless basiccard from ZeitControl.
EDIT2: I have set the timeout to 2000ms with no change in behavior. I'm trying to return a single byte of data and the system command I called also doesn't sound heavy.
Then I downloaded and attached the Android source and debugged. There was some segments it would still not go into - but the card seems to return null on valid commands unless I return some manually set SW1SW2 in which case that is the only thing received.
EDIT3: The system command I tried was:
192 14 0 0 0
(or C0 0E 00 00 00)
(or CLA INS P1 P2 Lc)
I'm not 100% sure I'm doing that one correctly, but I have tried with various lengths (up to 22) of Le and without Le as above and only without does it not give me 6700 (wrong Le/Lc)
Of course instead of 6700 it returns null it seems...
The other command is my own defined as 20 0A (value as Byte) and no P1/P2 specified in the .BAS file.
I call that one with:
32 10 1 0 1
(or 20 0A 01 00 01)
(or CLA INS Lc IDATA Le)
This should mean 1 byte data in, set to 0, and 1 byte expected out (+ SW1/SW2 as always).
(Setting P1/P2 gives 6700 so unless defined in the command declaration I dont think they should be there)
This also returns null. I Expect 00 90 00 to be returned here. (if I set "value" to 00 that is)
I'm using a HTC One X.
EDIT4:
MinSdk version = 14 and target 18.
if(bitcoinCard != null){
try {
String sentmsg, receivedmsg;
byte[] send = getBytes(commandBytes.getText().toString());
byte[] data = null;
if(send != null){
bitcoinCard.setTimeout(2000);
data = bitcoinCard.transceive(send);
}
//bitcoinCard.close();
/*if(data != null && data.length == 2)
{
mainLabel.setText("SW1SW2: " + (data[0] < 0 ? -data[0] +
128 : data[0]) + " " + (data[1] < 0 ? -data[1] + 128 : data[1]));
}else */if (data != null && send != null)
{
sentmsg = "" + (send[0] < 0 ? send[0] + 256 : send[0]);
for(int i = 1; i < send.length; i++)
{
sentmsg = sentmsg + " " + (send[i] < 0 ? send[i] +
256 : send[i]);
}
receivedmsg = "" + (data[0] < 0 ? data[0] + 256 : data[0]);
for(int i = 1; i < data.length; i++)
{
receivedmsg = receivedmsg + " " + (data[i] < 0 ? data[i] + 256 : data[i]);
}
mainLabel.setText("Sent: " + sentmsg + "\n" +
"Response: " +
receivedmsg);
}else
{
mainLabel.setText("Sent or received null.");
}
} catch (IOException e) {
mainLabel.setText("Tried to talk to card, but had error: " +
e.getMessage());
}
}
First, when you send APDUs you should use an IsoDep object (and not NfcA). Android should show both tag technologies as available for your card. The problem here is that Android will typically only activate the card in ISO 14443-4 protocol mode if you use IsoDep. Thus, wehn using NfcA, your card will not be ready to accept APDUs.
I just tested and this is at least the case on a Nexus S with Android 4.1.2. In fact trying to transceive using the NfcA object leads to TagLostExceptions with some cards and to some other really odd behavior with another card I tried.
Second, if you send
byte[] cmd = { (byte)0xC0, (byte)0x0E, (byte)0x00, (byte)0x00, (byte)0x00 };
I would expect the card to return the actual application ID. However, the answer to this command (i.e. <data> <SW1=61> <SW2=len>) does not comply to ISO 7816-4 (no data should be returned for a 61xx status code) so this might cause a problem.
UPDATE: I just tested this with a Nexus S (Android 4.1.2) and receiving such responses was not a problem.
Last, your other command (20 0A) is not what you expect it to be:
I strongly suggest you only use CLA bytes set to 0x00 or 0x80 unless you know what you are doing (use of secure messaging, use of logical channels, ...). Though, Android (at least with NXP's NFC chipset) does not care about the structure of the APDU, but your card might!
An APDU always has the form <CLA> <INS> <P1> <P2> [Lc [DATA]] <Le> (with the special case of <CLA> <INS> <P1> <P2>). So this means you cannot simply omit P1 and P2.
You are correct in that BasicCard will discard ODATA if SW<>9000 and SW1<>61.
I am developing an application where I have to connect to Bluetooth device on Android 4.3.
And I want to change the name of CC2541 Keyfob via the Android application.
My ideas is:
1.There has a Plain Text that I can type the name what I want in my Android application.
2.After I type the name, I push the button to send this text.
3.If the CC2541 receive this text from Android application , it will change the text in the deviceName[] of the following code in keyfobdemo.c:
static uint8 deviceName[] =
{
// complete name
0x0b, // length of first data structure (11 bytes excluding length byte)
0x09, // AD Type = Complete local name
0x4b, // 'K'
0x65, // 'e'
0x79, // 'y'
0x66, // 'f'
0x6f, // 'o'
0x62, // 'b'
0x64, // 'd'
0x65, // 'e'
0x6d, // 'm'
0x6f, // 'o'
};
The question like the following:
1.How to send the text data to CC2541 keyfob in Android application 4.3 ??
2.How to receive the text data on CC2541 side ??
3.Did I need to use any profile ??
Sorry about my English, and these question.
Thanks for your direction.
Edit
I have trying to use 0x2A00 to get the Device Name service , but it seen not working when I call the Device_Name function.
The Name_Service is null.
private static final UUID Device_Name_UUID = UUID.fromString("00002a00-0000-1000-8000-00805f9b34fb");
private static final UUID Write_UUID = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb");
public void Device_Name(){
BluetoothGattService Name_Service = mBluetoothGatt.getService(Write_UUID );
if(Name_Service == null) {
Log.d(TAG, "Name_Service service not found!");
return;
}
BluetoothGattCharacteristic DeviceName = Name_Service.getCharacteristic(Device_Name_UUID);
if(DeviceName == null) {
Log.d(TAG, "DeviceName charateristic not found!");
return;
}
}
Log.v(TAG, "readCharacteristic(DeviceName) = " + mBluetoothGatt.readCharacteristic(DeviceName));
String i = "123";
DeviceName.setValue(i);
Log.v(TAG, "writeCharacteristic(DeviceName) = " + mBluetoothGatt.writeCharacteristic(DeviceName));
it show the following Log:
V/BluetoothLeService( 3680): readCharacteristic(DeviceName) = true
V/BluetoothLeService( 3680): writeCharacteristic(DeviceName) = false
D/audio_hw_primary( 1752): found out /dev/snd/pcmC0D0p
W/audio_hw_primary( 1752): out_write() limiting sleep time 45351 to 23219
W/audio_hw_primary( 1752): out_write() limiting sleep time 34263 to 23219
W/audio_hw_primary( 1752): out_write() limiting sleep time 33696 to 23219
D/BtGatt.btif( 2646): btif_gattc_upstreams_evt: Event 3
I/BtGatt.btif( 2646): set_read_value unformat.len = 13
D/BtGatt.GattService( 2646): onReadCharacteristic() - address=90:59:AF:0B:8A:AB, status=0, length=13
D/BluetoothGatt( 3680): onCharacteristicRead() - Device=90:59:AF:0B:8A:AB UUID=00002a00-0000-1000-8000-00805f9b34fb Status=0
it read successful,and I can get the name of device.
And I reference the Bluetooth Page-Device Name , the format is UTF-8 String.
But it writeCharacteristic false.
I don't know about the Android BLE API, but I can tell you how this is supposed to work with Bluetooth Low Energy.
The device name is stored in the GATT server (a local database on the cc2541 device). If you connect to the BLE device you should be able to do a discover to figure out the structure of the database and find the ATT handle for the device name.
The GATT server is built up of attributes with a UUID (loosely defining the type of attribute) an attribute handle (the identifier used in this instance of the GATT server) and a value. According to [1] the UUID for the device name is 0x2A00. So you can search by type and find the handle with this UUID.
Once you have the UUID it's just a matter of using the GATT client in the Android API to send a write request to this handle with the new value
Edit: Looking at the API I think you should use getService(0x18, 0x00) [2] to get the primary service (which should contain the device name) and then writeCharacteristic[3] to update the name.
From [4] it looks like the code should look something like this (not tested):
public void writeCharacteristic(byte[] value) {
BluetoothGattService gap_service = mBluetoothGatt.getService(
UUID.fromString("00001800-0000-1000-8000-00805F9B34FB"));
if (gap_service == null) {
System.out.println("gap_service null";);
return;
}
BluetoothGattCharacteristic dev_name = gap_service.getCharacteristic(
UUID.fromString("00002A00-0000-1000-8000-00805F9B34FB"));
if (dev_name == null) {
System.out.println("dev_name null";);
return;
}
dev_name.setValue(value);
boolean status = mBluetoothGatt.writeCharacteristic(dev_name);
System.out.println("Write Status: " + status);
}
[1] bluetooth.org
[2] getService
[3] writeCharacteristic
[4] devzone.nordicsemi.com
This is my solution, adding to Vegar's background information about GATT, profiles, etc.
This is based on the simpleBLEPeripheral application in the CC2541 SDK, and the sensortag Android application. The simpleBLEPeripheral characteristic lets you read a multiple byte characteristic, I modified it to enable writes. The simpleGATTprofile.c simpleProfile_WriteAttrCB() method needs an additional case statement:
case SIMPLEPROFILE_CHAR5_UUID:
//Validate the value
// Make sure it's not too long
if ( len >= SIMPLEPROFILE_CHAR5_LEN )
{
status = ATT_ERR_INVALID_VALUE_SIZE;
}
//Write the value
if ( status == SUCCESS )
{
uint8 *pCurValue = (uint8 *)pAttr->pValue;
osal_memcpy(pCurValue+offset, pValue, len);
notifyApp = SIMPLEPROFILE_CHAR5;
}
break;
On the Android side, the following code is placed in DeviceActivity.java. Please forgive the messy code, it's a quick hack. It takes a string, finds the hex representation, which is then sent as a characteristic update to the CC2541.
void writeString() {
UUID servUuid = SensorTagGatt.UUID_STR_SERV;
UUID configUuid = SensorTagGatt.UUID_STR_DATA;
BluetoothGattService serv = mBtGatt.getService(servUuid);
BluetoothGattCharacteristic config = serv.getCharacteristic(configUuid);
int OAD_BLOCK_SIZE = 18;
int OAD_BUFFER_SIZE = OAD_BLOCK_SIZE + 2;
int GATT_WRITE_TIMEOUT = 300; // Milliseconds
String msg = new String();
byte[] mOadBuffer = hexStringToByteArray("e04fd020ea3a6910a2d808002b30309daabbccdd");
// Send block
config.setValue(mOadBuffer);
boolean success = mBtLeService.writeCharacteristic(config);
if (success) {
// Update stats
if (!mBtLeService.waitIdle(GATT_WRITE_TIMEOUT)) {
success = false;
msg = "GATT write timeout\n";
}
} else {
msg = "GATT writeCharacteristic failed\n";
}
if (!success) {
Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
}
}
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 key is to make sure your UUIDs match up, otherwise nothing will work.