imt-atlantique/YACL

How to encode and decode TAG based CBOR items

agualdron opened this issue · 6 comments

Hello,

I would like to use YACL to encode decimal fractions as described in RFC 7049 secction 2.4.3.
For example to encode the number 273.15 the CBOR is as follows:
C4 --> Tag 4
82 --> Array of length 2
21 --> -2
19 6AB3 --> 27315

I've tested this CBOR tag encoding in python implementations, but I would like to send this type of data from my ESP8266 using the arduino IDE.

I tried the following without success:

CBORArray data = CBORArray();  // Create an array
data.append(-2);                             // append the exponent
data.append(27315);                      // append the base
data.encode_type_num(CBOR_TAG, uint8_t(4)); // add the encoded CBOR at the beginning of the buffer

but i get the following error from the Arduino IDE:

CBOR.h:105:8: error: 'bool CBOR::encode_type_num(uint8_t, uint8_t)' is protected
   bool encode_type_num(uint8_t cbor_type, uint8_t val);

I'm not fully understand c++ language, but if you can give me some hints, I will find the way to make it work.

Thank you in advance.

#include "YACL.h"

long lastMsg = 0;

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  long now = millis();
  if (now - lastMsg > 2000) {
    Serial.println("");
    Serial.println("------------------------");
    lastMsg = now;

    CBORArray data = CBORArray(150);
    data.append(-4);
    data.append(31416);

    //data.encode_type_num(0xC, uint8_t(4));
    
    const char *cbor_encoded = (const char*)data.to_CBOR();
    
    Serial.print("CBOR encoded of 3.1416: ");

    unsigned int data_len = data.length();

    for (size_t i=0 ; i < data_len ; ++i) {
      if (cbor_encoded[i] < 0x10) {
        Serial.print('0');
      }
      Serial.print(cbor_encoded[i], HEX);
    }
    Serial.println("");
  }
}

I just manage to add the tag in a byte array and then mam-copy the previously CBOR encoded array of two items. It results in the desired encoded byte array, but, is there is an organized way to do this? in order to systematically append decimal fractions encoded this way into a CBOR array or pair.

Thank you.

#include "YACL.h"

long lastMsg = 0;

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  long now = millis();
  if (now - lastMsg > 2000) {
    Serial.println("");
    Serial.println("------------------------");
    lastMsg = now;

    CBORArray data = CBORArray(150);
    data.append(-2);
    data.append(27315);

    //data.encode_type_num(0xC, uint8_t(4));
    
    const char *cbor_encoded = (const char*)data.to_CBOR();
    unsigned int data_len = data.length();

    char tagged_data[data_len];

    tagged_data[0] = CBOR_TAG | 4;

    memcpy(tagged_data+1, cbor_encoded, data_len);
    data_len++;
    
    Serial.print("CBOR encoded of 3.1416: ");

    for (size_t i=0 ; i < data_len ; ++i) {
      if (tagged_data[i] < 0x10) {
        Serial.print('0');
      }
      Serial.print(tagged_data[i], HEX);
    }
    Serial.println("");
  }
}

Hello @agualdron

Thank you for your feedback!
Agreed: using memcpy() is not very clean :)

Making encode_type_num() public may be the solution (so that it can be used outside the classes, thus making your first code working).

I'll test that, and keep you informed

Alex.

I ended up adding a CBOR constructor for tagged CBOR objects. The syntax is the following:
CBOR(tag_value, tag_item)

So in your case, with the newest version of the library, you can write:

#include "YACL.h"

long lastMsg = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  long now = millis();
  if (now - lastMsg > 2000) {
    Serial.println("");
    Serial.println("------------------------");
    lastMsg = now;

    CBORArray tag_item = CBORArray(150);
    tag_item.append(-2);
    tag_item.append(27315);
    CBOR data = CBOR(0x04, tag_item);
    
    const uint8_t *cbor_encoded = data.to_CBOR();

    Serial.print("CBOR encoded of 273.15: ");

    for (size_t i=0 ; i < data.length() ; ++i) {
      if (cbor_encoded[i] < 0x10) {
        Serial.print('0');
      }
      Serial.print(cbor_encoded[i], HEX);
    }
    Serial.println("");
  }
}```

Hello @alexmrqt,

Thank you for your response, and support... I really appreciate...

I was trying the updated library, running the example code as is and I had the following error message:

/~/YACL/src/CBOR.cpp: In member function 'CBOR CBOR::get_tag_item() const':
/~/YACL/src/CBOR.cpp:1593:40: error: passing 'const CBOR' as 'this' argument of 'uint8_t* CBOR::get_buffer_begin()' discards qualifiers [-fpermissive]
  uint8_t *ele_begin = get_buffer_begin();
                                        ^

The error appeared in all arduino scripts with YACL, even the examples...

So, in file CBOR.cpp I made the following change:
from:

uint8_t *ele_begin = get_buffer_begin();

to

uint8_t *ele_begin = (uint8_t *)get_const_buffer_begin();

Then the error disappear, and now the code it's working like a charm.

Here is an example of making a list of tagged items, which is what I needed...

Thank you very much...

#include "YACL.h"

long lastMsg = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  long now = millis();
  if (now - lastMsg > 2000) {
    Serial.println("");
    Serial.println("------------------------");
    lastMsg = now;

    #define TAG CBOR_TAG | 0x04

    CBORArray item1_arr = CBORArray(10);
    item1_arr.append(-4);
    item1_arr.append(14142);
    CBOR item1 = CBOR(TAG, item1_arr);

    CBORArray item2_arr = CBORArray(10);
    item2_arr.append(-4);
    item2_arr.append(17321);
    CBOR item2 = CBOR(TAG, item2_arr);

    CBORArray item3_arr = CBORArray(10);
    item3_arr.append(-9);
    item3_arr.append(2236067977);
    CBOR item3 = CBOR(TAG, item3_arr);

    // CBOR ARRAY of Tagged Items
    CBORArray tag_array = CBORArray(150);
    tag_array.append(item1);
    tag_array.append(item2);
    tag_array.append(item3);

    const char *cbor_encoded = (const char*)tag_array.to_CBOR();
    unsigned int data_len = tag_array.length();

    Serial.print("CBOR encoded of 273.15: ");

    for (size_t i=0 ; i < data_len ; ++i) {
      if (cbor_encoded[i] < 0x10) {
        Serial.print('0');
      }
      Serial.print(cbor_encoded[i], HEX);
    }
    Serial.println("");
  }
}

Thank you a lot for the feedback!

The issue was that get_tag_item() was declared const, while it should not have been...
This should be fixed in v1.0.2.

Please do not hesitate tp open another issue if compilation still fails with the new version of the library.

Btw, I noticed that the compiler is much less forgiving when compiling for ESP8266/ESP32 than when compiling for official Arduino boards.
Some warnings in the latter case become errors in the former case... Now I know that I have to activate verbose compilation, and check for those warnings :)

The version 1.0.2 works even better...

Everything work as expected

Thank you very much...