Browse Source

examples: generic cmake support examples

Renz Christian Bagaporo 7 years ago
parent
commit
90f5432f2a

+ 5 - 0
.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

+ 1 - 0
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
 

+ 24 - 0
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}")

+ 69 - 0
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`

+ 8 - 0
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 .

+ 39 - 0
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 <stdio.h>
+#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();
+}

+ 6 - 0
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)

+ 36 - 0
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:
+<?xml version="1.0" encoding="UTF-8"?>
+<note>
+    <to>Tove</to>
+    <from>Jani</from>
+    <heading>Reminder</heading>
+    <body>Don't forget me this weekend!</body>
+</note>
+
+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`

+ 21 - 0
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)

+ 1 - 0
examples/build_system/cmake/import_lib/main/lib/tinyxml2

@@ -0,0 +1 @@
+Subproject commit 7e8e249990ec491ec15990cf95b6d871a66cf64a

+ 72 - 0
examples/build_system/cmake/import_lib/main/main.cpp

@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#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");
+}

+ 7 - 0
examples/build_system/cmake/import_lib/main/sample.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<note>
+  <to>Tove</to>
+  <from>Jani</from>
+  <heading>Reminder</heading>
+  <body>Don't forget me this weekend!</body>
+</note>

+ 6 - 0
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, 

+ 5 - 0
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

+ 1 - 1
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)

+ 1 - 1
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()

+ 1 - 1
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()