Jump to content
jcwhit

TBluetoothGattCharacteristic.SetValueAs will not reduce the size of the value array.

Recommended Posts

This issue shows up when writing a Characteristic from the Andorid app to the BT device. Here is the sequence using these TBluetoothGattCharactertistics methods

SetValueAsUint32, then 4 bytes are transmitted. 

SetValueAsUint64, then 8 bytes are transmitted

SetValueAsUint32, then 8 bytes are transmitted, the first four bytes contain the new 32 bits and the remaining 4 bytes contain the previous 64 bit value

*************************************************************************************************************

In System.Bluetooth we find this method @line 2717(10.4.2), which all of the above methods call

 

procedure TBluetoothGattCharacteristic.SetValueAs<T>(AValue: T; Offset: Integer);
var
  LBytes: TBytes;
begin
  LBytes := Value;
  if (Length(LBytes) < Offset + SizeOf(AValue)) then
      SetLength(LBytes, Offset + SizeOf(AValue));
  Move(AValue, LBytes[Offset], SizeOf(AValue));
  SetValue(LBytes);
end;

 

LBytes is always the last value sent (64 bits or 8bytes in the above example).  As long as offset is zero (0), following a 64bit value with a 32bit or less value,  will always result in SetLength not being called. And as long as offset is zero, this procedure can never reduce the size of LBytes, it can only increase the size of LBytes.

*****************************************************************************************************************

my work around was to create a method in my BT wrapper that

  1. creates a variable ClrValue of type TBytes
  2. set length to 1
  3. call the method SetValue(ClrValue)

This resets the TBluetoothGattCharactertistics property Value to be length 1 and thus the SetLength in the above code will always be called and the correct number of bytes will be transmitted.

*****************************************************************************************************************

I went back and checked 10.1 Update2 and the same code as above is there.  This may be expected behavior, I dont know,  Other than my little work around, I found no intrinsic way to change the array length and  thus the bytes transmitted.  If you call SetValueAsUint32, I would expect 4 bytes to be transmitted, regardless of what was transmitted before.

 

Edited by jcwhit

Share this post


Link to post

An update.  Using RAD Studio 12.3

 

Something changed, not sure when, but something changed and my workaround of resetting the length does not work any more.  I am developing my first 32bit embedded system.  As such I am sending both 32 bit and 64 bit characteristics.  While I could work around this in the embedded firmware, I prefer not to.  The BT portion of the embedded firmware is common to both the 8bit and 32bit systems and is much simpler to solve in the mobile software.

 

There is a sequence here:

1.  You call SetValueAsUint32(value: Uint32)

2.  The stack calls SetValueAs<T>(AValue: T; offsett: integer=0);

      this is the procedure described in the above post

3.  Then the stack calls SetValue(LBytes: TBytes);

      which actually places in the characteristic object the byte array to be transmitted

 

Now what I am doing is replacing (actually going around) the SetValueAsUint32 procedure call with this:

 

function TBLEActions.__SetValueAs<T>(AValue: T; Offset: Integer):TBytes;
// this will fix the problem of the characertistic sending the wrong number of bytes
var
  LBytes: TBytes;
  count: integer;
begin
     count:= Sizeof(AValue);                                       // this is for debug
     SetLength(LBytes, Offset + SizeOf(AValue));
     Move(AValue, LBytes[Offset], SizeOf(AValue));
     result:= LBytes;
end;

 

and the mobile code becomes this:

 

function TBLEActions.SendUint32(CharValue: TBluetoothGattCharacteristic; CmdValue: UInt32): boolean;
// send a UInt32 to the BLE Module via the passed Charateristic
// returns the status of the connection
var
   LBytes: TBytes;
begin
     if not(BLEConnected) then result:= FALSE
        else begin
             LBytes:= __SetValueAs<Uint32>(CmdValue,0);              // get the TByte array with the correct number of characters
             CharValue.SetValue(LBytes);                                          // call the procedure that places the byte array in the characteristic
             BTLEActions.WriteCharacteristicValue(CharValue);        // write out the characteristic
             LogCatWr('SendUint32='+IntToHex(CmdValue,8));
             result:= Wait4Notification;
        end;
end;

 

The upside to this is I can control the exact number of bytes in the characteristic.  Thus if I ever want to do a Uint16, I can or even a 5 or 6 byte array.

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×