zxubian
zxubian

Reputation: 35

Bluetooth LE: setting characteristic to byte array sends wrong values

I am using Bluetoothleapis.h to communicate with a custom Bluetooth Low Energy device.

The device is setup the following way:

I am able get proper values from characteristic#2. However, when I try to send data to characteristic#1, the device receives weird data.

The characteristic is responsible for 3 parameters of a real-life object (imagine a light with intensity, color, etc). (0,0,0) should respond to the "light" being off, but if I send the (0,0,0), I can see that the device receives something else (I cannot tell what exactly, but it is not off). The state does not seem to change no matter what values I send.

I have tried alternating between write and write-no-response, both produce the same result.

GetCharacteristicValue interestingly returns a charValueDataSize of 8, even though the characteristic is known to accept only 3 bytes. Coincidentally, the size for the 1-byte read-only characteristic is 9, for some reason.

I have tried limiting the size of the WriteValue to only 3 bytes, but in this case I get an invalid argument error. Answers elsewhere on StackOverflow have indicated that I need to use the one I get from GetCharacteristicValue, and transfer my data into there.

Given the fact that the real object's state does not change no matter which values are sent, I suspect that the problem is somewhere with the way I set up the byte array to transfer the data.

Furthermore, calling GetCharacteristicValue even after setting it returns an empty array.

I am not sure what values are actually being sent, and I lack the hardware to track them via Wireshark.

DWORD WriteValueToCharacteristic(__in const HANDLE deviceHandle, 
__in const CharacteristicData* pCharData, 
__in const UCHAR* writeBuffer,
__in const USHORT bufferSize,
__in const BOOL noResponse )
{
HRESULT hr;
PBTH_LE_GATT_CHARACTERISTIC pCharacteristic = pCharData->pCharacteristic;
USHORT charValueDataSize;

hr = BluetoothGATTGetCharacteristicValue
(
    deviceHandle,
    pCharacteristic,
    0,
    NULL,
    &charValueDataSize,
    BLUETOOTH_GATT_FLAG_NONE    
);

if (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA))
{
    Log(L"BluetoothGATTSetCharacteristicValue returned error %d", hr);
    FormatBluetoothError(hr);
    return -1;
}
PBTH_LE_GATT_CHARACTERISTIC_VALUE pWriteValue = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)HeapAlloc
(
    GetProcessHeap(), HEAP_ZERO_MEMORY, charValueDataSize + sizeof(BTH_LE_GATT_CHARACTERISTIC_VALUE)
);

if (pWriteValue == NULL)
{
    Log(L"Out of memory.");
    return -1;
}

hr = BluetoothGATTGetCharacteristicValue
(
    deviceHandle,
    pCharacteristic,
    charValueDataSize,
    pWriteValue,
    NULL,
    BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_DEVICE
);

memcpy(pWriteValue->Data, writeBuffer, bufferSize);
ULONG flags = noResponse == TRUE ? BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE : 0;

hr = BluetoothGATTSetCharacteristicValue
(
    deviceHandle, 
    pCharacteristic, 
    pWriteValue, 
    NULL,
    flags   
);

if (hr != S_OK)
{
    Log(L"BluetoothGATTSetCharacteristicValue returned error %d", hr);
    FormatBluetoothError(hr);
    return -1;
}
HeapFree(GetProcessHeap(), 0, pWriteValue);
return ERROR_SUCCESS;
}

SetCharacteristicValue returns S_OK, producing no errors.

Both reading and writing to the characteristic work fine when using a BLE app on Android.

Update 1

@Shubham pointed out it might be an endianness issue, so I tried to substitute memcpy for the following:

int j = 0;
int i = charValueDataSize - 1;
while (j < bufferSize)
{
    pWriteValue->Data[i] = writeBuffer[j];
    --i;
    ++j;
}

However, nothing changed.

Update 2

I have incorporated the changes as per emil's suggestion, and it worked! Posting the full code in case somebody else experiences the same issue.

Incidentally, even though the characteristic is marked as Writable: true, Writable-no-response: false, I need to set the flags to no-response in order for the values to get sent.

DWORD WriteValueToCharacteristic(__in const HANDLE deviceHandle, __in const CharacteristicData* pCharData, __in const UCHAR* writeBuffer, __in const USHORT bufferSize, __in const BOOL noResponse)
{
HRESULT hr;
PBTH_LE_GATT_CHARACTERISTIC pCharacteristic = pCharData->pCharacteristic;
USHORT charValueDataSize = 512;

PBTH_LE_GATT_CHARACTERISTIC_VALUE pWriteValue = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)HeapAlloc
(
    GetProcessHeap(), HEAP_ZERO_MEMORY, charValueDataSize + sizeof(BTH_LE_GATT_CHARACTERISTIC_VALUE)
);

if (pWriteValue == NULL)
{
    Log(L"Out of memory.");
    return -1;
}

hr = BluetoothGATTGetCharacteristicValue
(
    deviceHandle,
    pCharacteristic,
    (ULONG)charValueDataSize,
    pWriteValue,
    NULL,
    BLUETOOTH_GATT_FLAG_FORCE_READ_FROM_DEVICE
);
if (bufferSize > pWriteValue->DataSize)
{
   if(pWriteValue->DataSize == 0) 
    {
        pWriteValue->DataSize = bufferSize;
    }
}
// after the first write, DataSize stays as 3
//pWriteValue->DataSize here is 3, as expected
//buffer size is also 3
memcpy(pWriteValue->Data, writeBuffer, bufferSize);
ULONG flags = noResponse == TRUE ? BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE : 0;

hr = BluetoothGATTSetCharacteristicValue
(
    deviceHandle, 
    pCharacteristic, 
    pWriteValue, 
    NULL,
    flags   
);

if (hr != S_OK)
{
    Log(L"BluetoothGATTSetCharacteristicValue returned error %d", hr);
    FormatBluetoothError(hr);
    HeapFree(GetProcessHeap(), 0, pWriteValue);
    return -1;
}
HeapFree(GetProcessHeap(), 0, pWriteValue);
return ERROR_SUCCESS;
}

Upvotes: 0

Views: 1244

Answers (1)

Emil
Emil

Reputation: 18452

My suggestion is that you first set charValueDataSize to 512 + sizeof(BTH_LE_GATT_CHARACTERISTIC_VALUE) (maximum possible), and skip the initial read that would get the size. Then check pWriteValue->DataSize to get the actual size after a successful read. Also make sure you free your memory even in case of error.

Upvotes: 1

Related Questions