Storing Pointer Address Text into a Variable

Hardware: ESP32-WROOM
IDE: Arduino 2.3.6

Hello Again! I'm hoping for some tips on what I consider "advanced coding". The good news is; I've successfully connected
my cell phone to the ESP32, and able to read META data from locally stored MP3's, as well as YouTube streamed music.

My goal is to send the META data over USB Serial to Processing to display in a little program. The trouble is, I'm not sure how
to get the data stored at the Pointer address into a variable (array, string, etc.)? Everything I've read, and tried gives me an
error, such as:

invalid conversion from 'uint8_t' {aka 'unsigned char'} to 'char*' [-fpermissive]

I'll continue to Google/YouTube lesson my way through this, and post any resolutions. Help. Tips, and Ideas welcome!
Thank you! :ugeek:

#include "AudioTools.h"

#include "BluetoothA2DPSink.h"


I2SStream i2s;

BluetoothA2DPSink a2dp_sink(i2s);

char dataBT[100];


void avrc_metadata_callback(uint8_t id, const uint8_t *data){  

  Serial.printf("0x%x, %s\n", id, data);

   //strncpy(dataBT, (char*)data, sizeof(data));

  sprintf(dataBT, "0x%x, %s\n", id, data); 

}


void setup() {

  Serial.begin(115200);

  a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_PLAYING_TIME ); //| ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_PLAYING_TIME );

  a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);

  a2dp_sink.start("META_test");

}


void loop(){

  Serial.println(dataBT[0]);

  // Serial.println(dataBT[arrayCount]);  //use loop counter to display entire array

  delay(500);

}


it may be this way:

1 Like

Thanks for the response.

Unfortunately, it’s still complaining:

ā€invalid conversion from 'const uint8_t*' {aka 'const unsigned char*'} to 'const char*' [-fpermissive]ā€

I tried messing around with the array types (const uint8_t, int, char, etc.), and the null termination (ie: dataBT[MAX_METADATA_LEN -1] = 0; // =ā€˜0’

I feel like your idea is close based on what I’m researching, but I’m not sure where to

go as my experience with pointers is essentially only hours deep… :confused:

I’ll keep searching. Thanks again.

Interesting . Have a successful compile with:


char dataBT[100]; //MAX_METADATA_LEN];




void avrc_metadata_callback(uint8_t id, const uint8_t *data){  




  Serial.printf("0x%x, %s\n", id, data);

   //strncpy(dataBT, data, MAX_METADATA_LEN - 1);

   sprintf(dataBT, "0x%x, %s\n", id, data); //data, 100-1); //MAX_METADATA_LEN - 1);

   dataBT[100 -1] = '\0'; 

}

You should take note of and mention the line that the error is actually complaining about. In this case, despite what you said

I'm guessing the error looked more like

error: invalid conversion from 'const uint8_t*' {aka 'const unsigned char*'} to 'const char*' [-fpermissive]
      |  strncpy(dataBT, data, MAX_METADATA_LEN - 1);
      |                  ^~~~
      |                  |
      |                  const uint8_t* {aka const unsigned char*}```

Posting as <CODE/> even preserves all the spaces, so that the handy ^~~~ points to the exact thing at issue.

Two competing factors:

  • a2dp_sink.set_avrc_metadata_callback expects a function that takes a uint8_t pointer. That's the data argument in your function.
  • all the str functions like strcpy and strncpy expect a char pointer.

That's what the error means. Note the [-fpermissive] at the end; the -f indicates a compiler flag that you can enable. If you enable the permissive flag, the compiler will blindly do the conversion for you; it might work. Setting a flag like that is trivial if you're manually issuing compiler commands, but it's kind of annoying to do from within the IDE. More importantly, you should understand what it's actually doing, and do it the right way instead.

In your followup message with the successful compile, you side-step the error by avoiding strncpy, and use sprintf into the dataBT array; then NUL-terminate the array at the very last byte.

  • What if the data is shorter than 99 characters?
    "shorter than 99\n -- what happens here -- \0"
  • Or if it is longer than dataBT

You may already know that uint8_t and char are similar. That's why the permissive conversion is allowed. But a char* is expected to be NUL-terminated -- that's what makes it a C-string. A pointer to uint8_t is just "data" or "raw bytes". The fact that the callback function receives a pointer to bytes with no length argument implies some other structure in the payload itself. If so, what is it?

3 Likes

Great post, thanks. I’ll do my best to answer your questions!

You should take note of and mention the line that the error is actually complaining about. In this case, despite what you said. I'm guessing the error looked more like:

error: invalid conversion from 'const uint8_t*' {aka 'const unsigned char*'} to 'const char*' [-fpermissive]
      |  strncpy(dataBT, data, MAX_METADATA_LEN - 1);
      |                  ^~~~
      |                  |
      |                  const uint8_t* {aka const unsigned char*}```

Yes, the error looked very much like that; I’ll copy & paste the complete error message moving forward.

  • all the str functions like strcpy and strncpy expect a char pointer.

Noted. I figured this was the case based on what I read on web searches (char, or string data types).

That's what the error means. Note the [-fpermissive] at the end; the -f indicates a compiler flag that you can enable. If you enable the permissive flag, the compiler will blindly do the conversion for you; it might work.

I had no idea such a feature existed. A quick search lists a library called ā€œpermissive.hā€.
I’ll look into this, for sure.

…do it the right way instead.

In your followup message with the successful compile, you side-step the error by avoiding strncpy, and use sprintf into the dataBT array; then NUL-terminate the array at the very last byte.

Absolutely. As I learn, I will make the code more effective & efficient. I used strcpy just to
test whether I was able to get some data to store in the array. It’s not working properly,
however I am able to use Serial.Print(dataBT[xx]); to get individual characters to print.

  • What if the data is shorter than 99 characters?
    "shorter than 99\n -- what happens here -- \0"

    As an interim fix, I’ve added: dataBT[sizeof(data) - 1] = '\0';
    to terminate. I assumed the function would automatically terminate.
    If I remove the manual termination, the output is the same:

    ,T,h,e, ,G,r,a,p,e,v,i,n,e, ,(,L,i,v,e, ,O,n, ,J,o,o,l,s, ,H,o,l,l,a,n,d, ,H,o,o,t,e,n,a,n,n,y, ,/, ,2,0,0,6,),

  • Or if it is longer than dataBT

I’m not sure how to handle that, effectively? For now, I could make the array size
larger than necessary to prevent truncating the data.

You may already know that uint8_t and char are similar. That's why the permissive conversion is allowed. But a char* is expected to be NUL-terminated -- that's what makes it a C-string. A pointer to uint8_t is just "data" or "raw bytes".

The fact that the callback function receives a pointer to bytes with no length argument implies some other structure in the payload itself. If so, what is it?

This is above my pay grade at the moment. Would I check the source/library files (.h) to
get the answer?

Try to change the line this way:

This should remove the type mismatch error, but it carries a potential risk if your metadata is not a null-terminated string as indicated by @kenb4

1 Like

It got rid of the error, but now my output is blank:

6,7,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

sprintf gives me some sort of ā€˜readable’ data. Not even sure why strncpy doesn’t work?
I’m going to try memcpy with your casting suggestion.

Please show a full code using a code tags

1 Like

If you're using ESP32, there's a handy function -- for which all documentation seems to have disappeared? I have a slightly older board version installed, but try this

constexpr size_t useBuf = 12; 
char buf[useBuf + 4];  // a little extra on the end

void setup() {
  Serial.begin(115200);
  // could put delay here, or press reset to see output including boot messages
  for (int i = 0; i < useBuf + 1; i++) {  // one extra
    buf[i] = '!';
  }
  log_buf_i(reinterpret_cast<uint8_t*>(buf), sizeof(buf));  // print a little more
}

void loop() {
  static char t = 'A';
  static unsigned len;
  len += 3;
  char temp[len + 1];
  int i = 0;
  while (i < len) {
    temp[i++] = t++; 
  }
  temp[i] = 0;
  strncpy(buf, temp, useBuf);
  log_buf_i(reinterpret_cast<uint8_t*>(buf), sizeof(buf));
  if (len > useBuf) {
    for (;;);
  }
}

Note the comment about delay or reset; and that the _cast does the exact opposite of your error message. More importantly, to actually enable the log_buf_i function, you need to change the Core Debug Level from (the default) None to Info or more. That's about half-way down the Tools menu, near the top of the board-specific options. This is a build-time flag; so make that change and Upload.

You should see a could-be-prettier hex dump. Look at the right end of the first line (scroll if necessary)

0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x00, 0x00, 0x00, // !!!!!!!!!!!!!...

buf starts with all-zero because it's a global variable. The setup fills this most of the way with the ! placeholder. Non-printable characters are shown as plain . in the text portion.

Ignoring the hex values, look at all the text output

 !!!!!!!!!!!!!...
 ABC.........!...
 DEFGHI......!...
 JKLMNOPQR...!...
 STUVWXYZ[\]^!...
 _`abcdefghij!...

If you're plucking bytes from the air, assume they're malicious. In C/C++ that means first of all: don't trust the length. And that means always use a function that limits the length, like strncpy instead of just strcpy.

But strncpy has its quirks. Read the description carefully, and corroborate it with the output shown. Some prefer strlcpy where available.

Use log_buf_i to print a hex dump of the uint8_t *data (no cast necessary). Don't try to print too much at once; you don't want to reach into invalid memory and cause a crash. But this is an easy way to see text and the hex values of any non-printable characters that might indicate structure.

void avrc_metadata_callback(uint8_t id, const uint8_t *data) {
  Serial.print("avrc\t");
  Serial.println(id, HEX);  // apparently it's a bit mask
  log_buf_i(data, 48);  // to start
}

If I'm looking at the right library the docs say

Note that data2 is actually a char* string, so even though ESP_AVRC_MD_ATTR_PLAYING_TIME is documented as the milliseconds of media duration you'll need to parse it before doing math on it.

So all these precautions are for naught. But now you know.

I have no idea what that is. Instead, search for "gcc fpermissive" if you want to know more. But again, not necessary to use it.

1 Like

First off, here’s the latest code (updated in o.p. as well):confused:

#include "AudioTools.h"

#include "BluetoothA2DPSink.h"


I2SStream i2s;

BluetoothA2DPSink a2dp_sink(i2s);

char dataBT[100];


void avrc_metadata_callback(uint8_t id, const uint8_t *data){  

  Serial.printf("0x%x, %s\n", id, data);

   //strncpy(dataBT, (char*)data, sizeof(data));

  sprintf(dataBT, "0x%x, %s\n", id, data); 

}


void setup() {

  Serial.begin(115200);

  a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_PLAYING_TIME ); //| ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_PLAYING_TIME );

  a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);

  a2dp_sink.start("META_test");

}


void loop(){

  Serial.println(dataBT[0]);

  // Serial.println(dataBT[arrayCount]);  //use loop counter to display entire array

  delay(500);

}


This is the response after uploading the code, and connecting my phone via Bluetooth,with your callback suggestion (original callback disabled, of course…):

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:4980
load:0x40078000,len:16612
load:0x40080400,len:3480
entry 0x400805b4

avrc    40
avrc    40
avrc    1
avrc    40
avrc    1
avrc    2
avrc    40

The top code Serial Monitor was blank as copied in your post. Once I added the Bluetooth
portion of the code, I got the following (code copied below, compliler set to ā€œVerboseā€):

[ 22853][D][BluetoothA2DPCommon.cpp:30] ccall_app_gap_callback(): [BT_AV] ccall_app_gap_callback

[ 22862][I][BluetoothA2DPSink.cpp:458] app_gap_callback(): [BT_AV] event: 16

[ 22921][D][BluetoothA2DPCommon.cpp:30] ccall_app_gap_callback(): [BT_AV] ccall_app_gap_callback

[ 22930][I][BluetoothA2DPSink.cpp:458] app_gap_callback(): [BT_AV] event: 21

[ 22959][D][BluetoothA2DPSink.cpp:1098] app_a2d_callback(): [BT_AV] app_a2d_callback

[ 22967][D][BluetoothA2DPSink.cpp:1102] app_a2d_callback(): [BT_AV] app_a2d_callback ESP_A2D_CONNECTION_STATE_EVT[ 22977][D][BluetoothA2DPSink.cpp:350] app_work_dispatch(): [BT_API] app_work_dispatch event 0x0, param len 20
[ 22987][D][BluetoothA2DPSink.cpp:1098] app_a2d_callback(): [BT_AV] app_a2d_callback
[ 23004][D][BluetoothA2DPCommon.cpp:513] app_work_dispatched(): [BT_AV] app_work_dispatched

[ 23012][D][BluetoothA2DPSink.cpp:43] ccall_av_hdl_a2d_evt(): [BT_AV] ccall_av_hdl_a2d_evt

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);
//char dataBT[100];

constexpr size_t useBuf = 12;
char buf[useBuf + 4];  // a little extra on the end

void avrc_metadata_callback(uint8_t id, const uint8_t *data){  

 // Serial.printf("0x%x, %s\n", id, data);
 // strncpy(dataBT, (char*)data, sizeof(data));
 // sprintf(dataBT, "0x%x, %s\n", id, data); 

}


void setup() {

 Serial.begin(115200);

//a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_PLAYING_TIME ); //| ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_PLAYING_TIME );
  //a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
  a2dp_sink.start("META_test");

for (int i = 0; i < useBuf + 1; i++) {  // one extra
  buf[i] = '!';
}

 log_buf_i(reinterpret_cast<uint8_t*>(buf), sizeof(buf));  // print a little  more
}

void loop(){
static char t = 'A';

  static unsigned len;
  len += 3;
  char temp[len + 1];

  int i = 0;
  while (i < len) {
    temp[i++] = t++; 
  }

  temp[i] = 0;
  strncpy(buf, temp, useBuf);
  log_buf_i(reinterpret_cast<uint8_t*>(buf), sizeof(buf));

  if (len > useBuf) {
    for (;;);
  }
}

I’ll mess around with some ideas, and see I’ve overlooked anything.

This looks like multiple callbacks, printing the id, but no hex dump of the data. My suggestion was hard-coded to print 48 bytes (three lines of 16). Are you sure the Core Debug Level was set correctly?

The other messages

would indicate that it was at least Debug, to show both [D]ebug and [I]nfo messages. They don't seem that relevant to your issue, though.

BTW, if you're curious about those AVRC metadata IDs, searching for the enum mentioned in those docs

$ rg --no-line-number -B 9 -A 9 ESP_AVRC_MD_ATTR_PLAYING_TIME ~/.arduino15/packages/esp32/

(~/.arduino15 is on Linux) finds several variations of

/// AVRC metadata attribute mask
typedef enum {
    ESP_AVRC_MD_ATTR_TITLE = 0x1,                 /*!< title of the playing track */
    ESP_AVRC_MD_ATTR_ARTIST = 0x2,                /*!< track artist */
    ESP_AVRC_MD_ATTR_ALBUM = 0x4,                 /*!< album name */
    ESP_AVRC_MD_ATTR_TRACK_NUM = 0x8,             /*!< track position on the album */
    ESP_AVRC_MD_ATTR_NUM_TRACKS = 0x10,           /*!< number of tracks on the album */
    ESP_AVRC_MD_ATTR_GENRE = 0x20,                /*!< track genre */
    ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40          /*!< total album playing time in miliseconds */
} esp_avrc_md_attr_mask_t;

You got mostly PLAYING_TIME and TITLE; each separately, no combinations.

1 Like

This is what I received this time. I must not have set the debug properly?!

0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x00, 0x00, 0x00, // !!!!!!!!!!!!!...

=========== After Setup Start ============

INTERNAL Memory Info:

------------------------------------------

  Total Size        :   268984 B ( 262.7 KB)

  Free Bytes        :    78632 B (  76.8 KB)

  Allocated Bytes   :   178488 B ( 174.3 KB)

  Minimum Free Bytes:    76032 B (  74.2 KB)

  Largest Free Block:    61428 B (  60.0 KB)

------------------------------------------

GPIO Info:

------------------------------------------

  GPIO : BUS_TYPE[bus/unit][chan]

  --------------------------------------  

     1 : UART_TX[0]

     3 : UART_RX[0]

============ After Setup End =============

0x41, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, // ABC.........!...

0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, // DEFGHI......!...

0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, // JKLMNOPQR...!...

0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x21, 0x00, 0x00, 0x00, // STUVWXYZ[\]^!...

0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x21, 0x00, 0x00, 0x00, // _`abcdefghij!...

Ln 54, Col 2

ESP32-WROOM-DA Module

on COM5

2

I did however need to add the Bluetooth connection in order to get the memory dump (as per code listed above).

Thanks so much!

Some major progress since my last post, with the help of Google and analyzing
the responses from this thread.

The new code looks like this, and I’m able to print in within loop via global variables,
and also send/receive in Processing:

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

#define MAX_METADATA_LEN 128

char track_title[MAX_METADATA_LEN]  = "";
char track_artist[MAX_METADATA_LEN] = "";
char track_album[MAX_METADATA_LEN]  = "";

bool got_title  = false, got_artist = false, got_album  = false;
float value1 = 1.23, value2 = 4.56;

// copy from AVRCP data to buffer
void copy_metadata(char *dest, size_t dest_size, const uint8_t *src) {
if (!dest || !src || dest_size == 0) return;

strncpy(dest, (const char *)src, dest_size - 1);
dest[dest_size - 1] = '\0'; //null-terminate
}

// Function to check if all metadata is ready
void check_and_update_display() {

if (got_title && got_artist && got_album) {
// Reset flags for next song
got_title = got_artist = got_album = false;
}
}

// AVRCP Metadata Callback
void avrc_metadata_callback(uint8_t attr_id, const uint8_t *data) {
if (!data) return;

switch (attr_id) {
case ESP_AVRC_MD_ATTR_TITLE:
copy_metadata(track_title, sizeof(track_title), data);
got_title = true;
break;

case ESP_AVRC_MD_ATTR_ARTIST:
  copy_metadata(track_artist, sizeof(track_artist), data);
  got_artist = true;
  break;

case ESP_AVRC_MD_ATTR_ALBUM:
  copy_metadata(track_album, sizeof(track_album), data);
  got_album = true;
  break;

default:
  // Ignore other metadata for now
  break;

}

// Check if all fields are ready
check_and_update_display();
}

void setup() {

Serial.begin(115200);
a2dp_sink.start("META_test");
a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_PLAYING_TIME | ESP_AVRC_MD_ATTR_ALBUM);
a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
}

void loop(){
Serial.print(value1);        Serial.print(',');
Serial.print(value2);        Serial.print(',');
Serial.printf(track_title);  Serial.print(',');
Serial.printf(track_artist); Serial.print(',');
Serial.printf(track_album);  Serial.print(',');
Serial.println(1010);
}


I’m able to read in the data, and manipulate numbers and text data…BUT, of course there’s
still an issue, albeit a new issue!

After about ten seconds, Processing begins to jumble the data, whether the song is updated,
or not. Code below. I’ll keep trying to figure this out, but it’s very odd as the Serial Monitor
in the Arduino IDE does not exhibit this anomaly. I’ll try the Processing forum as well.

Thanks again for any tips on this snag!

Processing Code for Serial Event and data splitting:


import processing.serial.*;

Serial dataPort;
String serialData, artist, album, songTitle;
float value1, value2;
String[] numsString;

void setup(){

size(500, 500);  background(0);

dataPort = new Serial(this, Serial.list()[3], 115200);
dataPort.bufferUntil(10);

delay(500);

}

void draw(){

background(0);
dataACK();

} //end draw

void serialEvent(Serial xCOM){

serialData = xCOM.readStringUntil(10); // Read until newline
if(serialData != null){
  serialData = trim(serialData);         // Remove whitespace/newline
}
else{serialData = "";}

}

void dataACK(){

numsString = split(serialData, ',');
printArray(numsString);


value1 = float(numsString[0]);
artist = numsString[2];

fill(255); textSize(22);
text(value1, 40, 100);
text(artist, 40, 200);
}

Did a bunch of troubleshooting.

Arduino UNO works with BT info removed, no delay required.

ESP32-WROOM needs delay to work (?).

Added delay(100); at the end of the loop() function.

I’ll add the BT code back in with delay and see what gives…

Final ESP32-WROOM code!


#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

#define MAX_METADATA_LEN 128

char track_title[MAX_METADATA_LEN]  = "";
char track_artist[MAX_METADATA_LEN] = "";
char track_album[MAX_METADATA_LEN]  = "";

bool got_title  = false, got_artist = false, got_album  = false;
float value1 = 1.23, value2 = 4.56;

// copy from AVRCP data to buffer
void copy_metadata(char *dest, size_t dest_size, const uint8_t *src) {
  if (!dest || !src || dest_size == 0) return;

  strncpy(dest, (const char *)src, dest_size - 1);
  dest[dest_size - 1] = '\0'; //null-terminate
}

// Function to check if all metadata is ready
void check_and_update_display() {

  if (got_title && got_artist && got_album) {
  // Reset flags for next song
  got_title = got_artist = got_album = false;
  }
}

// AVRCP Metadata Callback
void avrc_metadata_callback(uint8_t attr_id, const uint8_t *data) {
  if (!data) return;

  switch (attr_id) {
  case ESP_AVRC_MD_ATTR_TITLE:
    copy_metadata(track_title, sizeof(track_title), data);
    got_title = true;
  break;

  case ESP_AVRC_MD_ATTR_ARTIST:
    copy_metadata(track_artist, sizeof(track_artist), data);
    got_artist = true;
  break;

  case ESP_AVRC_MD_ATTR_ALBUM:
    copy_metadata(track_album, sizeof(track_album), data);
    got_album = true;
  break;

  default:
  // Ignore other metadata for now
  break;

  }

  // Check if all fields are ready
  check_and_update_display();
}

void setup() {

  Serial.begin(115200);
  a2dp_sink.start("META_test");
  a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_PLAYING_TIME | ESP_AVRC_MD_ATTR_ALBUM);
  a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
}

void loop(){
  Serial.print(value1);       Serial.print(',');
  Serial.print(value2);       Serial.print(',');
  Serial.print(track_title);  Serial.print(',');
  Serial.print(track_artist); Serial.print(',');
  Serial.println(track_album); 
  delay(50);
}

Thank you to everyone that helped out!