From 90f5432f2ad2a4646117dfeedae7b0c978eed299 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 11 Nov 2018 15:36:24 +0800 Subject: [PATCH] examples: generic cmake support examples --- .gitmodules | 5 ++ examples/README.md | 1 + .../cmake/idf_as_lib/CMakeLists.txt | 24 +++++++ .../build_system/cmake/idf_as_lib/README.md | 69 ++++++++++++++++++ .../build_system/cmake/idf_as_lib/build.sh | 8 +++ examples/build_system/cmake/idf_as_lib/main.c | 39 ++++++++++ .../cmake/import_lib/CMakeLists.txt | 6 ++ .../build_system/cmake/import_lib/README.md | 36 ++++++++++ .../cmake/import_lib/main/CMakeLists.txt | 21 ++++++ .../cmake/import_lib/main/lib/tinyxml2 | 1 + .../cmake/import_lib/main/main.cpp | 72 +++++++++++++++++++ .../cmake/import_lib/main/sample.xml | 7 ++ .../cmake/import_lib/partitions_example.csv | 6 ++ .../cmake/import_lib/sdkconfig.defaults | 5 ++ .../get-started/hello_world/CMakeLists.txt | 2 +- .../native_ota_example/main/CMakeLists.txt | 2 +- .../simple_ota_example/main/CMakeLists.txt | 2 +- 17 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 examples/build_system/cmake/idf_as_lib/CMakeLists.txt create mode 100644 examples/build_system/cmake/idf_as_lib/README.md create mode 100755 examples/build_system/cmake/idf_as_lib/build.sh create mode 100644 examples/build_system/cmake/idf_as_lib/main.c create mode 100644 examples/build_system/cmake/import_lib/CMakeLists.txt create mode 100644 examples/build_system/cmake/import_lib/README.md create mode 100644 examples/build_system/cmake/import_lib/main/CMakeLists.txt create mode 160000 examples/build_system/cmake/import_lib/main/lib/tinyxml2 create mode 100644 examples/build_system/cmake/import_lib/main/main.cpp create mode 100644 examples/build_system/cmake/import_lib/main/sample.xml create mode 100644 examples/build_system/cmake/import_lib/partitions_example.csv create mode 100644 examples/build_system/cmake/import_lib/sdkconfig.defaults diff --git a/.gitmodules b/.gitmodules index eb60d7eaa5..a9489d8a1a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,6 +61,11 @@ [submodule "components/protobuf-c/protobuf-c"] path = components/protobuf-c/protobuf-c url = https://github.com/protobuf-c/protobuf-c + [submodule "components/unity/unity"] path = components/unity/unity url = https://github.com/ThrowTheSwitch/Unity + +[submodule "examples/build_system/cmake/import_lib/main/lib/tinyxml2"] + path = examples/build_system/cmake/import_lib/main/lib/tinyxml2 + url = https://github.com/leethomason/tinyxml2 diff --git a/examples/README.md b/examples/README.md index b164fc30fb..62e0b73bd5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,6 +15,7 @@ The examples are grouped into subdirectories by category. Each category director * `storage` contains examples showing data storage methods using SPI flash or external storage like the SD/MMC interface. * `system` contains examples which demonstrate some internal chip features, or debugging & development tools. * `wifi` contains examples of advanced Wi-Fi features. (For network protocol examples, see `protocols` instead.) +* `build_system` contains examples of build system features # Using Examples diff --git a/examples/build_system/cmake/idf_as_lib/CMakeLists.txt b/examples/build_system/cmake/idf_as_lib/CMakeLists.txt new file mode 100644 index 0000000000..ba0176f1d7 --- /dev/null +++ b/examples/build_system/cmake/idf_as_lib/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.5) +project(idf_as_lib C) + +# The source file main.c contains app_main() definition +add_executable(${CMAKE_PROJECT_NAME}.elf main.c) + +# Provides idf_import_components() and idf_link_components() +include($ENV{IDF_PATH}/tools/cmake/idf_functions.cmake) + +# Create artifacts used for flashing the project to target chip +set(IDF_BUILD_ARTIFACTS ON) +set(IDF_PROJECT_EXECUTABLE ${CMAKE_PROJECT_NAME}.elf) +set(IDF_BUILD_ARTIFACTS_DIR ${CMAKE_BINARY_DIR}) + +# Trim down components included in the build. Although freertos and spi_flash are the ones needed by the application +# itself, the bootloader and esptool_py components are also needed in order to create the artifacts to be used +# for flashing to the target chip +set(IDF_COMPONENTS freertos spi_flash bootloader esptool_py) + +# Wraps add_subdirectory() to create library targets for components, and then return them using the specified variable +idf_import_components(components $ENV{IDF_PATH} esp-idf) + +# Wraps target_link_libraries() to link processed components by idf_import_components to target +idf_link_components(${CMAKE_PROJECT_NAME}.elf "${components}") \ No newline at end of file diff --git a/examples/build_system/cmake/idf_as_lib/README.md b/examples/build_system/cmake/idf_as_lib/README.md new file mode 100644 index 0000000000..19506a8fac --- /dev/null +++ b/examples/build_system/cmake/idf_as_lib/README.md @@ -0,0 +1,69 @@ +# Using ESP-IDF in Custom CMake Projects + +This example illustrates using ESP-IDF components as libraries in custom CMake projects. This builds +an equivalent application to the `hello_world` example under `examples/get-started/hello_world`. + +## Example Flow + +Users looking at this example should focus on the [top-level CMakeLists.txt file](./CMakeLists.txt). This builds an +application that can run on targets without relying on the typical ESP-IDF application template. The application itself +follows a similar code flow to the aforementioned `hello_world` example. + +### Output + +``` +Hello world! +This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 0, 4MB external flash +Restarting in 10 seconds... +Restarting in 9 seconds... +Restarting in 8 seconds... +Restarting in 7 seconds... +Restarting in 6 seconds... +Restarting in 5 seconds... +Restarting in 4 seconds... +Restarting in 3 seconds... +Restarting in 2 seconds... +Restarting in 1 seconds... +Restarting in 0 seconds... +``` + +## Building this Example + +To build this example, run the following commands from this directory: + +```bash +# Create a build directory, and change location to that directory. +mkdir build; cd build +# Invoke CMake, specifying the top-level CMakeLists.txt directory and toolchain file to use. This will generate +# the build system files. +cmake .. -DCMAKE_TOOLCHAIN_FILE=$IDF_PATH/tools/cmake/toolchain-esp32.cmake -DIDF_TARGET=esp32 +# Build using the generated build system files. +cmake --build . +``` + +Or, execute `build.sh` script, which contains the same commands mentioned above. + +## Flashing and Running this Example + +To flash this example, we will have to invoke `esptool.py` and `idf_monitor.py` manually. While still in the build directory: + +### Flashing to target + +```bash +# Write project binaries to flash. +esptool.py --port /dev/ttyUSB0 write_flash @flash_project_args +``` + +### Running on target + +```bash +# Monitor the output of the flashed firmware. +idf_monitor.py --port /dev/ttyUSB0 idf_as_lib.elf +``` + +Of course, you should replace the specified ports in the commands specified above to the proper one where your device +is connected. + +--- + +There is a discussion on using ESP-IDF in custom CMake projects in the programming guide under `API Guides` -> `Build System (CMake)` -> `Using ESP-IDF in Custom CMake Projects` diff --git a/examples/build_system/cmake/idf_as_lib/build.sh b/examples/build_system/cmake/idf_as_lib/build.sh new file mode 100755 index 0000000000..23de02c836 --- /dev/null +++ b/examples/build_system/cmake/idf_as_lib/build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# +# Build this example, which does not use that standard IDF app template. See README.md for +# more information about the build and how to run this example on the target once built. + +mkdir build; cd build +cmake .. -DCMAKE_TOOLCHAIN_FILE=$IDF_PATH/tools/cmake/toolchain-esp32.cmake -DIDF_TARGET=esp32 +cmake --build . \ No newline at end of file diff --git a/examples/build_system/cmake/idf_as_lib/main.c b/examples/build_system/cmake/idf_as_lib/main.c new file mode 100644 index 0000000000..59f0aa0f56 --- /dev/null +++ b/examples/build_system/cmake/idf_as_lib/main.c @@ -0,0 +1,39 @@ +/* Hello World 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 "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_spi_flash.h" + +void app_main() +{ + printf("Hello world!\n"); + + /* Print chip information */ + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ", + chip_info.cores, + (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : ""); + + printf("silicon revision %d, ", chip_info.revision); + + printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); + + for (int i = 10; i >= 0; i--) { + printf("Restarting in %d seconds...\n", i); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + printf("Restarting now.\n"); + fflush(stdout); + esp_restart(); +} diff --git a/examples/build_system/cmake/import_lib/CMakeLists.txt b/examples/build_system/cmake/import_lib/CMakeLists.txt new file mode 100644 index 0000000000..e145bd43ae --- /dev/null +++ b/examples/build_system/cmake/import_lib/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(import_cmake_lib) diff --git a/examples/build_system/cmake/import_lib/README.md b/examples/build_system/cmake/import_lib/README.md new file mode 100644 index 0000000000..e24090a87c --- /dev/null +++ b/examples/build_system/cmake/import_lib/README.md @@ -0,0 +1,36 @@ +# Import Third-Party CMake Library Example + +This example demonstrates how to import third-party CMake libraries. + +## Example Flow + +[tinyxml2](https://github.com/leethomason/tinyxml2) is a +a small C++ XML parser. It is imported, without modification, for use in the project's `main` component (see the `main` component's [CMakeLists.txt](main/CMakeLists.txt)). To demonstrate the library being used, a sample XML is embedded into the project. +This sample XML is then read and parsed later on using `tinyxml2`. + +### Output + +``` +I (317) example: Setting up... +I (317) example: Copying sample XML to filesystem... +I (647) example: Reading XML file +I (657) example: Read XML data: + + + Tove + Jani + Reminder + Don't forget me this weekend! + + +I (667) example: Parsed XML data: + +To: Tove +From: Jani +Heading: Reminder +Body: Don't forget me this weekend! +I (677) example: Example end +``` +--- + +There is a discussion on importing third-party CMake libraries in the programming guide under `API Guides` -> `Build System (CMake)` -> `Using Third-Party CMake Projects with Components` diff --git a/examples/build_system/cmake/import_lib/main/CMakeLists.txt b/examples/build_system/cmake/import_lib/main/CMakeLists.txt new file mode 100644 index 0000000000..92d44e3249 --- /dev/null +++ b/examples/build_system/cmake/import_lib/main/CMakeLists.txt @@ -0,0 +1,21 @@ +set(COMPONENT_SRCS "main.cpp") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +set(COMPONENT_EMBED_TXTFILES "sample.xml") + +register_component() + +# Build static library, do not build test executables +option(BUILD_SHARED_LIBS OFF) +option(BUILD_TESTING OFF) + +# Import tinyxml2 targets +add_subdirectory(lib/tinyxml2) + +# Propagate compile settings to tinyxml2 +target_include_directories(tinyxml2 PRIVATE ${IDF_INCLUDE_DIRECTORIES}) +target_compile_options(tinyxml2 PRIVATE "${IDF_COMPILE_OPTIONS}") +target_compile_options(tinyxml2 PRIVATE "${IDF_CXX_COMPILE_OPTIONS}") + +# Link tinyxml2 to main component +target_link_libraries(${COMPONENT_TARGET} tinyxml2) diff --git a/examples/build_system/cmake/import_lib/main/lib/tinyxml2 b/examples/build_system/cmake/import_lib/main/lib/tinyxml2 new file mode 160000 index 0000000000..7e8e249990 --- /dev/null +++ b/examples/build_system/cmake/import_lib/main/lib/tinyxml2 @@ -0,0 +1 @@ +Subproject commit 7e8e249990ec491ec15990cf95b6d871a66cf64a diff --git a/examples/build_system/cmake/import_lib/main/main.cpp b/examples/build_system/cmake/import_lib/main/main.cpp new file mode 100644 index 0000000000..6df76dcaf3 --- /dev/null +++ b/examples/build_system/cmake/import_lib/main/main.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "lib/tinyxml2/tinyxml2.h" + +using namespace tinyxml2; + +static const char *TAG = "example"; + +// Handle of the wear levelling library instance +static wl_handle_t s_wl_handle = WL_INVALID_HANDLE; + +// Mount path for the partition +const char *base_path = "/spiflash"; + +extern "C" void app_main(void) +{ + // Do example setup + ESP_LOGI(TAG, "Setting up..."); + esp_vfs_fat_mount_config_t mount_config; + mount_config.max_files = 4; + mount_config.format_if_mount_failed = true; + mount_config.allocation_unit_size = CONFIG_WL_SECTOR_SIZE; + + esp_err_t err = esp_vfs_fat_spiflash_mount(base_path, "storage", &mount_config, &s_wl_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err)); + return; + } + + // The sample XML is embedded binary data. Create a file first containing the embedded + // so it can be accessed. + ESP_LOGI(TAG, "Copying sample XML to filesystem..."); + + extern const char data_start[] asm("_binary_sample_xml_start"); + extern const char data_end[] asm("_binary_sample_xml_end"); + FILE *f = fopen("/spiflash/sample.xml", "wb"); + + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + fwrite(data_start, sizeof(char), data_end - data_start + 1, f); + fclose(f); + + // Now that the file is created, load it using tinyxml2 and parse + ESP_LOGI(TAG, "Reading XML file"); + + XMLDocument data; + data.LoadFile("/spiflash/sample.xml"); + + XMLPrinter printer; + data.Print(&printer); + + ESP_LOGI(TAG, "Read XML data:\n%s", printer.CStr()); + + const char* to_data = data.FirstChildElement("note")->FirstChildElement("to")->GetText(); + const char* from_data = data.FirstChildElement("note")->FirstChildElement("from")->GetText(); + const char* heading_data = data.FirstChildElement("note")->FirstChildElement("heading")->GetText(); + const char* body_data = data.FirstChildElement("note")->FirstChildElement("body")->GetText(); + + ESP_LOGI(TAG, "Parsed XML data:\n\nTo: %s\nFrom: %s\nHeading: %s\nBody: %s", + to_data, from_data, heading_data, body_data); + + ESP_LOGI(TAG, "Example end"); +} \ No newline at end of file diff --git a/examples/build_system/cmake/import_lib/main/sample.xml b/examples/build_system/cmake/import_lib/main/sample.xml new file mode 100644 index 0000000000..fa0c0a5b64 --- /dev/null +++ b/examples/build_system/cmake/import_lib/main/sample.xml @@ -0,0 +1,7 @@ + + + Tove + Jani + Reminder + Don't forget me this weekend! + \ No newline at end of file diff --git a/examples/build_system/cmake/import_lib/partitions_example.csv b/examples/build_system/cmake/import_lib/partitions_example.csv new file mode 100644 index 0000000000..3e9543311e --- /dev/null +++ b/examples/build_system/cmake/import_lib/partitions_example.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, fat, , 528K, diff --git a/examples/build_system/cmake/import_lib/sdkconfig.defaults b/examples/build_system/cmake/import_lib/sdkconfig.defaults new file mode 100644 index 0000000000..f30f322c62 --- /dev/null +++ b/examples/build_system/cmake/import_lib/sdkconfig.defaults @@ -0,0 +1,5 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_APP_OFFSET=0x10000 diff --git a/examples/get-started/hello_world/CMakeLists.txt b/examples/get-started/hello_world/CMakeLists.txt index 8a022b8486..519e5a1d7b 100644 --- a/examples/get-started/hello_world/CMakeLists.txt +++ b/examples/get-started/hello_world/CMakeLists.txt @@ -3,4 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(hello-world) +project(hello-world) \ No newline at end of file diff --git a/examples/system/ota/native_ota_example/main/CMakeLists.txt b/examples/system/ota/native_ota_example/main/CMakeLists.txt index a700e0b3b4..fa4644e455 100644 --- a/examples/system/ota/native_ota_example/main/CMakeLists.txt +++ b/examples/system/ota/native_ota_example/main/CMakeLists.txt @@ -2,6 +2,6 @@ set(COMPONENT_SRCS "native_ota_example.c") set(COMPONENT_ADD_INCLUDEDIRS ".") # Embed the server root certificate into the final binary -set(COMPONENT_EMBED_TXTFILES ${PROJECT_PATH}/server_certs/ca_cert.pem) +set(COMPONENT_EMBED_TXTFILES ${IDF_PROJECT_PATH}/server_certs/ca_cert.pem) register_component() diff --git a/examples/system/ota/simple_ota_example/main/CMakeLists.txt b/examples/system/ota/simple_ota_example/main/CMakeLists.txt index 8adb69a8f7..98a7208f86 100644 --- a/examples/system/ota/simple_ota_example/main/CMakeLists.txt +++ b/examples/system/ota/simple_ota_example/main/CMakeLists.txt @@ -3,6 +3,6 @@ set(COMPONENT_ADD_INCLUDEDIRS ".") # Embed the server root certificate into the final binary -set(COMPONENT_EMBED_TXTFILES ${PROJECT_PATH}/server_certs/ca_cert.pem) +set(COMPONENT_EMBED_TXTFILES ${IDF_PROJECT_PATH}/server_certs/ca_cert.pem) register_component()