Alex Wayne
Alex Wayne

Reputation: 186984

Managing buffers of binary data in C++

I'm trying to create a simple binary format to transmit over a BlueToothLE module on Arduino. I'm trying to describe properties of a list of objects. And for starters I'm trying to transmit just a single property.

The format I'm attempting to encode and pass around is as follows.

namePropertyID, nameLength, nameString...

So given a name of "Bob"

0x01     0x03     0x42 0x6F 0x62
nameID   3 chars  "B"  "o"  "b"

But when I pass the buffer around, it seems to mutate.

before I pass it, it reads:

0x01 0x03 0x42 0x6F 0x62

But after I pass it, it reads:

0x00 0x3C 0x18 0x04 0x00

Program.h

typedef enum {
  InfoTypeName = 0x01
} InfoType;

class Program {
  public:
    char *name;

    uint8_t * data();
    uint8_t dataLen();
};

Program.cpp

#include "Program.h"

uint8_t* Program::data() {
  uint8_t nameLength = strlen(name);
  uint8_t buff[dataLen()];

  buff[0] = InfoTypeName;
  buff[1] = nameLength;

  for (uint8_t i = 0; i < nameLength; i++) {
    buff[i+2] = (uint8_t)name[i];
  }

  // First check of data, things look ok.
  for (uint8_t i = 0; i < nameLength+2; i++) {
    Serial.print(F(" 0x")); Serial.print(buff[i], HEX);
  }
  Serial.println();

  return buff;
}

uint8_t Program::dataLen() {
  return strlen(name) + 2;
}

Elsewhere I pass this to the Bluetooth library:

BTLEserial.write(program.data(), program.dataLen());

Which is implemented like so, and is printing out seemingly incorrect data:

size_t Adafruit_BLE_UART::write(uint8_t * buffer, uint8_t len)
{
  Serial.print(F("\tWriting out to BTLE:"));
  for (uint8_t i=0; i<len; i++) {
    Serial.print(F(" 0x")); Serial.print(buffer[i], HEX);
  }
  Serial.println();

  // actually sends the data over bluetooth here...
}

So a few questions:

Upvotes: 1

Views: 862

Answers (1)

juanchopanza
juanchopanza

Reputation: 227370

The problem is that in Program::data(), buff is a local variable. You are returning a pointer to its first element, which is a dangling pointer at the call side. You need to ensure the buffer you export is something that stays alive for long enough. There are different ways of doing this, but I am not entirely familiar with the limitations arduino places on what parts of the C and C++ standard libraries you can use.

The simplest approach could be to reserve a buffer in main, and pass that around to the code that populates it and consumes it. Alternatively, you could give your Program class a buffer data member. The main problem is going to be ensuring that the buffer is large enough for different messages.

I would first try something like this:

void create_msg_(const char* name, uint8_t buff, size_t size)
{
  // populate buff with the message
}

void send_msg(const char* name)
{
  size_t size = strlen(name) + 2;
  uint8_t buff[size]; // VLA extension, not std C++
  create_msg_(name, buff, size);
  BTLEserial.write(buff, size);
}

Upvotes: 5

Related Questions