diff --git a/.gitmodules b/.gitmodules index 70bd0c478e..8366568747 100644 --- a/.gitmodules +++ b/.gitmodules @@ -74,3 +74,7 @@ [submodule "components/bt/host/nimble/nimble"] path = components/bt/host/nimble/nimble url = ../../espressif/esp-nimble.git + +[submodule "components/cbor/tinycbor"] + path = components/cbor/tinycbor + url = ../../intel/tinycbor.git diff --git a/components/cbor/CMakeLists.txt b/components/cbor/CMakeLists.txt new file mode 100644 index 0000000000..5aaaaf80fb --- /dev/null +++ b/components/cbor/CMakeLists.txt @@ -0,0 +1,21 @@ +idf_component_register(SRCS "tinycbor/src/cborencoder_close_container_checked.c" + "tinycbor/src/cborencoder.c" + "tinycbor/src/cborerrorstrings.c" + "tinycbor/src/cborparser_dup_string.c" + "tinycbor/src/cborparser.c" + "tinycbor/src/cborpretty_stdio.c" + "tinycbor/src/cborpretty.c" + "tinycbor/src/cbortojson.c" + "tinycbor/src/cborvalidation.c" + "tinycbor/src/open_memstream.c" + INCLUDE_DIRS "port/include" + PRIV_INCLUDE_DIRS "tinycbor/src") + +# for open_memstream.c +target_compile_definitions(${COMPONENT_LIB} PRIVATE "__GLIBC__") + +# cbortojson.c:378:17: assignment discards 'const' qualifier from pointer target type +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-discarded-qualifiers") + +# cborvalidation.c:429:22: 'valf' may be used uninitialized +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-maybe-uninitialized") diff --git a/components/cbor/component.mk b/components/cbor/component.mk new file mode 100644 index 0000000000..9f9d37c361 --- /dev/null +++ b/components/cbor/component.mk @@ -0,0 +1,8 @@ +COMPONENT_SUBMODULES += tinycbor +COMPONENT_ADD_INCLUDEDIRS := port/include +COMPONENT_SRCDIRS := tinycbor/src +COMPONENT_PRIV_INCLUDEDIRS := tinycbor/src + +tinycbor/src/open_memstream.o: CFLAGS += -D__GLIBC__ +tinycbor/src/cbortojson.o: CFLAGS += -Wno-discarded-qualifiers +tinycbor/src/cborvalidation.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/components/cbor/port/include/cbor.h b/components/cbor/port/include/cbor.h new file mode 100644 index 0000000000..c8e6ccf483 --- /dev/null +++ b/components/cbor/port/include/cbor.h @@ -0,0 +1,2 @@ +#include "../../tinycbor/src/cbor.h" +#include "../../tinycbor/src/cborjson.h" diff --git a/components/cbor/tinycbor b/components/cbor/tinycbor new file mode 160000 index 0000000000..d2dd95cb88 --- /dev/null +++ b/components/cbor/tinycbor @@ -0,0 +1 @@ +Subproject commit d2dd95cb8841d88d5a801e3ef9c328fd6200e7bd diff --git a/examples/protocols/cbor/CMakeLists.txt b/examples/protocols/cbor/CMakeLists.txt new file mode 100644 index 0000000000..8b524ec386 --- /dev/null +++ b/examples/protocols/cbor/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(cbor) diff --git a/examples/protocols/cbor/Makefile b/examples/protocols/cbor/Makefile new file mode 100644 index 0000000000..7b394132d2 --- /dev/null +++ b/examples/protocols/cbor/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := cbor + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/cbor/README.md b/examples/protocols/cbor/README.md new file mode 100644 index 0000000000..3b6f81a1a2 --- /dev/null +++ b/examples/protocols/cbor/README.md @@ -0,0 +1,56 @@ +# CBOR Example +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Overview + +The [CBOR](https://en.wikipedia.org/wiki/CBOR)(Concise Binary Object Representation) is a binary data serialization format which is similar to JSON but with smaller footprint. This example will illustrate how to encode and decode CBOR data using the APIs provided by [tinycbor](https://github.com/intel/tinycbor). + +For detailed information about how CBOR encoding and decoding works, please refer to [REF7049](https://tools.ietf.org/html/rfc7049) or [cbor.io](http://cbor.io/); + +## How to use example + +### Hardware Required + +This example should be able to run on any commonly available ESP32 development board. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build and flash the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +```bash +I (320) example: encoded buffer size 67 +I (320) example: convert CBOR to JSON +[{"chip":"esp32","unicore":false,"ip":[192,168,1,100]},3.1400001049041748,"simple(99)","2019-07-10 09:00:00+0000","undefined"] +I (340) example: decode CBOR manually +Array[ + Map{ + chip + esp32 + unicore + false + ip + Array[ + 192 + 168 + 1 + 100 + ] + } + 3.14 + simple(99) + 2019-07-10 09:00:00+0000 + undefined +] +``` + +## Troubleshooting + +For more API usage, please refer to [tinycbor API](https://intel.github.io/tinycbor/current/). + +(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.) diff --git a/examples/protocols/cbor/main/CMakeLists.txt b/examples/protocols/cbor/main/CMakeLists.txt new file mode 100644 index 0000000000..31dfa610cb --- /dev/null +++ b/examples/protocols/cbor/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "cbor_example_main.c" + INCLUDE_DIRS "") diff --git a/examples/protocols/cbor/main/cbor_example_main.c b/examples/protocols/cbor/main/cbor_example_main.c new file mode 100644 index 0000000000..3807ce0e3d --- /dev/null +++ b/examples/protocols/cbor/main/cbor_example_main.c @@ -0,0 +1,226 @@ +/* CBOR Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "esp_log.h" +#include "cbor.h" + +static const char *TAG = "example"; + +#define CBOR_CHECK(a, str, goto_tag, ret_value, ...) \ + do \ + { \ + if ((a) != CborNoError) \ + { \ + ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret = ret_value; \ + goto goto_tag; \ + } \ + } while (0) + +static void indent(int nestingLevel) +{ + while (nestingLevel--) { + printf(" "); + } +} + +static void dumpbytes(const uint8_t *buf, size_t len) +{ + while (len--) { + printf("%02X ", *buf++); + } +} + +/** + * Decode CBOR data manuallly + */ +static CborError example_dump_cbor_buffer(CborValue *it, int nestingLevel) +{ + CborError ret = CborNoError; + while (!cbor_value_at_end(it)) { + CborType type = cbor_value_get_type(it); + + indent(nestingLevel); + switch (type) { + case CborArrayType: { + CborValue recursed; + assert(cbor_value_is_container(it)); + puts("Array["); + ret = cbor_value_enter_container(it, &recursed); + CBOR_CHECK(ret, "enter container failed", err, ret); + ret = example_dump_cbor_buffer(&recursed, nestingLevel + 1); + CBOR_CHECK(ret, "recursive dump failed", err, ret); + ret = cbor_value_leave_container(it, &recursed); + CBOR_CHECK(ret, "leave container failed", err, ret); + indent(nestingLevel); + puts("]"); + continue; + } + case CborMapType: { + CborValue recursed; + assert(cbor_value_is_container(it)); + puts("Map{"); + ret = cbor_value_enter_container(it, &recursed); + CBOR_CHECK(ret, "enter container failed", err, ret); + ret = example_dump_cbor_buffer(&recursed, nestingLevel + 1); + CBOR_CHECK(ret, "recursive dump failed", err, ret); + ret = cbor_value_leave_container(it, &recursed); + CBOR_CHECK(ret, "leave container failed", err, ret); + indent(nestingLevel); + puts("}"); + continue; + } + case CborIntegerType: { + int64_t val; + ret = cbor_value_get_int64(it, &val); + CBOR_CHECK(ret, "parse int64 failed", err, ret); + printf("%lld\n", (long long)val); + break; + } + case CborByteStringType: { + uint8_t *buf; + size_t n; + ret = cbor_value_dup_byte_string(it, &buf, &n, it); + CBOR_CHECK(ret, "parse byte string failed", err, ret); + dumpbytes(buf, n); + puts(""); + free(buf); + continue; + } + case CborTextStringType: { + char *buf; + size_t n; + ret = cbor_value_dup_text_string(it, &buf, &n, it); + CBOR_CHECK(ret, "parse text string failed", err, ret); + puts(buf); + free(buf); + continue; + } + case CborTagType: { + CborTag tag; + ret = cbor_value_get_tag(it, &tag); + CBOR_CHECK(ret, "parse tag failed", err, ret); + printf("Tag(%lld)\n", (long long)tag); + break; + } + case CborSimpleType: { + uint8_t type; + ret = cbor_value_get_simple_type(it, &type); + CBOR_CHECK(ret, "parse simple type failed", err, ret); + printf("simple(%u)\n", type); + break; + } + case CborNullType: + puts("null"); + break; + case CborUndefinedType: + puts("undefined"); + break; + case CborBooleanType: { + bool val; + ret = cbor_value_get_boolean(it, &val); + CBOR_CHECK(ret, "parse boolean type failed", err, ret); + puts(val ? "true" : "false"); + break; + } + case CborHalfFloatType: { + uint16_t val; + ret = cbor_value_get_half_float(it, &val); + CBOR_CHECK(ret, "parse half float type failed", err, ret); + printf("__f16(%04x)\n", val); + break; + } + case CborFloatType: { + float val; + ret = cbor_value_get_float(it, &val); + CBOR_CHECK(ret, "parse float type failed", err, ret); + printf("%g\n", val); + break; + } + case CborDoubleType: { + double val; + ret = cbor_value_get_double(it, &val); + CBOR_CHECK(ret, "parse double float type failed", err, ret); + printf("%g\n", val); + break; + } + case CborInvalidType: { + ret = CborErrorUnknownType; + CBOR_CHECK(ret, "unknown cbor type", err, ret); + break; + } + } + + ret = cbor_value_advance_fixed(it); + CBOR_CHECK(ret, "fix value failed", err, ret); + } + return CborNoError; +err: + return ret; +} + + +void app_main(void) +{ + CborEncoder root_encoder; + CborParser root_parser; + CborValue it; + uint8_t buf[100]; + + // Initialize the outermost cbor encoder + cbor_encoder_init(&root_encoder, buf, sizeof(buf), 0); + + // Create an array containing several items + CborEncoder array_encoder; + CborEncoder map_encoder; + cbor_encoder_create_array(&root_encoder, &array_encoder, 5); // [ + // 1. Create a map containing several pairs + cbor_encoder_create_map(&array_encoder, &map_encoder, 3); // { + // chip:esp32 + cbor_encode_text_stringz(&map_encoder, "chip"); + cbor_encode_text_stringz(&map_encoder, "esp32"); + // unicore:false + cbor_encode_text_stringz(&map_encoder, "unicore"); + cbor_encode_boolean(&map_encoder, false); + // ip:[192,168,1,100] + cbor_encode_text_stringz(&map_encoder, "ip"); + CborEncoder array2; + cbor_encoder_create_array(&map_encoder, &array2, 4); // [ + // Encode several numbers + cbor_encode_uint(&array2, 192); + cbor_encode_uint(&array2, 168); + cbor_encode_uint(&array2, 1); + cbor_encode_uint(&array2, 100); + cbor_encoder_close_container(&map_encoder, &array2); // ] + cbor_encoder_close_container(&array_encoder, &map_encoder); // } + // 2. Encode float number + cbor_encode_float(&array_encoder, 3.14); + // 3. Encode simple value + cbor_encode_simple_value(&array_encoder, 99); + // 4. Encode a string + cbor_encode_text_stringz(&array_encoder, "2019-07-10 09:00:00+0000"); + // 5. Encode a undefined value + cbor_encode_undefined(&array_encoder); + cbor_encoder_close_container(&root_encoder, &array_encoder); // ] + + // If error happend when encoding, then this value should be meaningless + ESP_LOGI(TAG, "encoded buffer size %d", cbor_encoder_get_buffer_size(&root_encoder, buf)); + + // Initialize the cbor parser and the value iterator + cbor_parser_init(buf, sizeof(buf), 0, &root_parser, &it); + + ESP_LOGI(TAG, "convert CBOR to JSON"); + // Dump the values in JSON format + cbor_value_to_json(stdout, &it, 0); + puts(""); + + ESP_LOGI(TAG, "decode CBOR manually"); + // Decode CBOR data manully + example_dump_cbor_buffer(&it, 0); +} diff --git a/examples/protocols/cbor/main/component.mk b/examples/protocols/cbor/main/component.mk new file mode 100644 index 0000000000..0b9d7585e7 --- /dev/null +++ b/examples/protocols/cbor/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/tools/ci/build_examples.sh b/tools/ci/build_examples.sh index 9be1525b3f..9dc831d43c 100755 --- a/tools/ci/build_examples.sh +++ b/tools/ci/build_examples.sh @@ -99,7 +99,7 @@ build_example () { local EXAMPLE_DIR=$(dirname "${MAKE_FILE}") local EXAMPLE_NAME=$(basename "${EXAMPLE_DIR}") - + # Check if the example needs a different base directory. # Path of the Makefile relative to $IDF_PATH local MAKE_FILE_REL=${MAKE_FILE#"${IDF_PATH}/"} @@ -182,7 +182,8 @@ echo -e "\nFound issues:" IGNORE_WARNS="\ library/error\.o\ \|\ -Werror\ -\|error\.d\ +\|.*error.*\.o\ +\|.*error.*\.d\ \|reassigning to symbol\ \|changes choice state\ \|Compiler version is not supported\ diff --git a/tools/ci/build_examples_cmake.sh b/tools/ci/build_examples_cmake.sh index cc5f3adc00..9a8751ae26 100755 --- a/tools/ci/build_examples_cmake.sh +++ b/tools/ci/build_examples_cmake.sh @@ -145,7 +145,7 @@ build_example () { cat ${BUILDLOG} popd - grep -i "error\|warning" "${BUILDLOG}" 2>&1 | grep -v "error.c.obj" >> "${LOG_SUSPECTED}" || : + grep -i "error\|warning" "${BUILDLOG}" 2>&1 >> "${LOG_SUSPECTED}" || : } EXAMPLE_NUM=0 @@ -175,6 +175,7 @@ echo -e "\nFound issues:" # 'Compiler and toochain versions is not supported' from crosstool_version_check.cmake IGNORE_WARNS="\ library/error\.o\ +\|.*error.*\.c\.obj\ \|\ -Werror\ \|error\.d\ \|reassigning to symbol\