ESP32: CBOR

Introduction

In this tutorial we will learn how to perform CBOR serialization and deserialization, using the ESP32, the Arduino core and the Nlohmann/json library. For a detailed guide on how to get started with the library, including installation instructions for the ESP32, please check this previous tutorial.

CBOR (Concise Binary Object Representation) is a data format whose design goals include the possibility of small code size, small message size and extensibility without the need for version negotiation [1]. You can read the RFC here.

It’s important to mention that CBOR is supported in multiple programming languages, making it a feasible choice as data exchange format between different applications. You can check a list of implementations in different languages here.

Note that the code we are covering here extends beyond the scope of the ESP32 programming. Being Nlohmann/json a generic C++ library, most of the code we are going to see below can be used in any C++ program.

The tests shown on this post were performed on a ESP32-E FireBeetle board from DFRobot. The Arduino core version used was 2.0.0 and the Arduino IDE version was 1.8.15, working on Windows 8.1. The version of the Nlohmann/json library used was 3.10.2.

CBOR serialization

We will start our code with the json.hpp library include.

#include <json.hpp>

Then we will analyze the implementation of the Arduino setup, where we will write the rest of our code. We will start by opening a serial connection, to be able to output the results of our program.

Serial.begin(115200);

Then we will define a string containing a JSON object. For exemplification purposes, this JSON object will be the structure of a person.

char str[] = R"(
    {
      "name": "Jake",
      "age": 23,
      "address": {
        "street": "St. Street",
        "code": "1234-127"
      }
    }
)";

Now that we have our JSON string, we will parse it to a json object. To do so, we will use the parse static method, passing as input the string. As output, we will obtain the parsed json object.

nlohmann::json obj = nlohmann::json::parse(str);

To serialize the json object to a CBOR payload, we simply need to call the to_cbor static method, passing as input the json. As output we obtain a vector of bytes.

std::vector<std::uint8_t> cborArray = nlohmann::json::to_cbor(obj);

To finalize, we will iterate through all the bytes of our vector. We will iterate twice: the first one to print each byte in hexadecimal format and the second to print them in decimal format.

for(uint8_t i : cborArray){ 
  Serial.printf("%02X ", i);
}
 
Serial.println("\n--------");
for(uint8_t i : cborArray){ 
  Serial.printf("%d ", i);
}

The complete code is available below.

#include <json.hpp>
 
void setup() {
 
  Serial.begin(115200);
 
  char str[] = R"(
    {
      "name": "Jake",
      "age": 23,
      "address": {
        "street": "St. Street",
        "code": "1234-127"
      }
    }
  )";
 
  nlohmann::json obj = nlohmann::json::parse(str);
  std::vector<std::uint8_t> cborArray = nlohmann::json::to_cbor(obj);
 
  for(uint8_t i : cborArray){ 
    Serial.printf("%02X ", i);
  }
 
  Serial.println("\n--------");
  for(uint8_t i : cborArray){ 
    Serial.printf("%d ", i);
  }
}
 
void loop() {}

To test the code, simply compile it and upload it to your ESP32. When the procedure finishes, open the IDE serial monitor. You should see a result similar to figure 1, which shows the bytes both in hexadecimal and decimal formats.

Result of the CBOR serialization, performed with the ESP32.
Figure 1 – Result of the CBOR serialization, performed with the ESP32.

CBOR deserialization

In this section we will learn how to deserialize a CBOR payload into a json object. As usual, we will start by the library include.

#include <json.hpp>

Moving on to the Arduino setup, we will start by opening a serial connection.

Serial.begin(115200);

Then we will define a vector of bytes that will hold our CBOR payload. For simplicity, we will use the bytes printed to the serial port in the previous section.

std::vector<std::uint8_t> cborArray = {
    163, 103, 97, 100, 100, 114, 101, 115, 115, 162, 100, 99, 
    111, 100, 101, 104, 49, 50, 51, 52, 45, 49, 50, 55, 102, 
    115, 116, 114, 101, 101, 116, 106, 83, 116, 46, 32, 83, 116, 
    114, 101, 101, 116, 99, 97, 103, 101, 23, 100, 110, 97, 109, 
    101, 100, 74, 97, 107, 101
};

To perform the deserialization of this byte vector to a json object, we simply need to call the from_cbor static method. As input we pass the vector with the bytes and as output we will obtain the deserialized json object.

nlohmann::json obj = nlohmann::json::from_cbor(cborArray);

To confirm that everything worked as expected, we will now serialize the json object to a string and print it to the serial port. Naturally, since we used the binary payload from the previous section, we expect to obtain the same JSON object representing a person. Note however that the order of the fields and the indentation will differ, but all the properties and their respective values will be the same.

As a reminder from this post where we covered how to serialize a json object to a string, we simply need to call the dump method on our json object. Optionally, we can pass an integer as input to indicate the indentation level of the JSON string. Recall also that we will obtain a std::string as output, meaning that we need to call the c_str method before printing it to the serial port.

std::string serializedObject = obj.dump(3);
Serial.println(serializedObject.c_str());

The whole code is shown below.

#include <json.hpp>
 
void setup() {
 
  Serial.begin(115200);
 
  std::vector<std::uint8_t> cborArray = {
    163, 103, 97, 100, 100, 114, 101, 115, 115, 162, 100, 99, 
    111, 100, 101, 104, 49, 50, 51, 52, 45, 49, 50, 55, 102, 
    115, 116, 114, 101, 101, 116, 106, 83, 116, 46, 32, 83, 116, 
    114, 101, 101, 116, 99, 97, 103, 101, 23, 100, 110, 97, 109, 
    101, 100, 74, 97, 107, 101
  };
 
  nlohmann::json obj = nlohmann::json::from_cbor(cborArray);
 
  std::string serializedObject = obj.dump(3);
  Serial.println(serializedObject.c_str());
}
 
void loop() {}

Upon running the code, you should obtain a result like the one shown below on figure 2. As can be seen, we obtained our original JSON object, which we serialized in the previous section.

Result of the CBOR deserialization, performed with the ESP32.
Figure 2 – Result of the CBOR deserialization, performed with the ESP32.

Suggested ESP32 readings

References

[1] https://cbor.io/

Leave a Reply