Help with "parsing" ANCS Data Source response

Using the BLEPeripheral library, my Arduino Zero (TinyScreen+) smartwatch successfully retrieves Notification Source characteristics, but I wanted to take it a step further to retrieve some of the Notification Attributes (in this case the title). I know I'm getting the Data Source response, but I suspect that I'm not "parsing" the data correctly because my output is truncated severely: the output only contains the 5th to 12th characters (out of a possible 14). I hope to get the TS+ to display the title to a max of 20 characters (including the null termination). Here is the relevant code:

char notificationLine2[20] = ""; //declaration of global variable for the output

struct AncsAttributes {          //for the Data Source response
  unsigned char commandId;
  unsigned long notificationUid;
  unsigned char attributeId;
  unsigned short attributeLen;
  char attributeValue[20];

void ancsNotificationSourceCharacteristicValueUpdated(BLECentral& central, BLERemoteCharacteristic& characteristic) {


unsigned long uid = notification.notificationUid; //construction of Control Point request
  byte buffer[8];
  buffer[0] = 0;
  buffer[4] = (uid >> 24) & 0xFF;
  buffer[3] = (uid >> 16) & 0xFF;
  buffer[2] = (uid >> 8) & 0xFF;
  buffer[1] = uid & 0xFF;
  ancsControlPointCharacteristic.write((const unsigned char*)buffer, 8); 

void ancsDataSourceCharacteristicCharacteristicValueUpdated(BLECentral& central, BLERemoteCharacteristic& characteristic) {
  struct AncsAttributes attributeList;
  memcpy(&attributeList, characteristic.value(), sizeof(attributeList));
  strcpy(notificationLine2, attributeList.attributeValue);
  notificationLine2[19] = '\0'; 

Any help would be appreciated!

I'm not sure you give us enough code to make sense of what you do

One question and one suggestion:

what does characteristic.value() returns - is that a pointer to a AncsAttributes struct?

  memcpy(&attributeList, [color=blue]characteristic.value()[/color], sizeof(attributeList));

Not sure if this is what bites you but when dealing with structures, the standard does not guarantee the compiler won't move attributes around to gain better byte boundary alignement (architecture dependent) to optimize data access for example.

So when you use memcpy() with data organized the way you think it is (maybe coming from somewhere else through BT) and force fill in the data, if the compiler played tricks on you, you are toast!

Luckily C++ offers an option to inform the compiler to not mess around with what you have done.

Sometimes you can coerce your compiler into not using the processor’s normal alignment rules by using a pragma, usually #pragma pack. GCC and clang have an attributepacked you can attach to individual structure declarations; GCC has an -fpack-struct option for entire compilations.

Do not do this casually, as it forces the generation of more expensive and slower code.

(source structure alignment and padding)

In your case you could try to enforce the structure

//for the Data Source response
struct __attribute__ ((packed)) AncsAttributes { // see
  unsigned char commandId;
  unsigned long notificationUid;
  unsigned char attributeId;
  unsigned short attributeLen;
  char attributeValue[20];

just an idea....

Thanks for the quick response! characteristic.value holds the response from the Data source. I don't think I could rework the struct to account for padding because the attributes from the DS are given in a particular order. Your suggestion worked in that the first few chars are no longer cut off. It still outputs only 12 chars though. That's something I'll leave for another day :slight_smile: