Example Analysis

This section will take the program Blink as an example to analyse the file structure and coding rules of a real project in detail. The Blink program implements the LED blinking effect, and the project is located in the directory examples/get-started/blink, which contains a source file, configuration files, and several compilation scripts.

The smart light project introduced in this book is based on this example program. Functions will be gradually added in later chapters to finally complete it.

📝 Source code

In order to demonstrate the entire development process, the Blink program has been copied to esp32c3-iot-projects/device_firmware/1_blink.

The directory structure of the blink project files is shown in Figure 4.15.

Figure 4.15. File directory structure of the blink project

The blink project contains only one main directory, which is a special component that must be included as described in section 4.3.2. The main directory is mainly used to store the implementation of the app_main() function, which is the entry point to the user program.The blink project does not include the components directory, because this example only needs to use the components that come with ESP-IDF and does not require additional components. The CMakeLists.txt included in the blink project is used to guide the compilation process, while Kconfig.projbuild is used to add configuration items for this example program in menuconfig. Other unnecessary files will not affect the compilation of the code, so they will not be discussed here. A detailed introduction to the blink project files is as follows.

/*blink.c includes the following header files*/
#include <stdio.h>              //Standard C library header file
#include "freertos/freeRTOS.h"  //FreeRTOS main header file
#include "freertos/task.h"      //FreeRTOS Task header file
#include "sdkconfig.h"          //Configuration header file generated by kconfig
#include "driver/gpio.h"        //GPIO driver header file

The source file blink.c contains a series of header files corresponding to function declarations. ESP-IDF generally follows the order of including standard library header files, FreeRTOS header files, driver header files, other component header files, and project header files. The order in which header files are included may affect the final compilation result, so try to follow the default rules. It should be noted that sdkconfig.h is automatically generated by kconfig and can only be configured through the command idf.py menuconfig. Direct modification of this header file will be overwritten.

/*You can select the GPIO corresponding to the LED in idf.py menuconfig, and the modification result of menuconfig is that the value of CONFIG_BLINK_GPIO will be changed. You can also directly modify the macro definition here, and change CONFIG_BLINK_GPIO to a fixed value.*/
void app_main(void)
    /*Configure IO as the GPIO default function, enable pull-up mode, and disable input and output modes*/
    /*Set GPIO to output mode*/
    gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
        while(1) {
        /*Print log*/
        printf("Turning off the LED\n");
        /*Turn off the LED (output low level)*/
        gpio_set_level(BLINK_GPIO, 0);
        /*Delay (1000 ms)*/
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        printf("Turning on the LED\n");
        /*Turn on the LED (output high level)*/
        gpio_set_level(BLINK_GPIO, 1);
        vTaskDelay(1000 / portTICK_PERIOD_MS);

The app_main() function in the Blink example program serves as the entry point for user programs. It is a simple function with no parameters and no return value. This function is called after the system has completed initialisation, which includes tasks such as initialising the log serial port, configuring single/dual core, and configuring the watchdog.

The app_main() function runs in the context of a task named main. The stack size and priority of this task can be adjusted in menuconfig → Componentconfig → Common ESP-related.

For simple tasks like blinking an LED, all the necessary code can be implemented directly in the app_main() function. This typically involves initialising the GPIO corresponding to the LED and using a while(1) loop to toggle the LED on and off. Alternatively, you can use FreeRTOS API to create a new task that handles the LED blinking. Once the new task is successfully created, you can exit the app_main() function.

The content of main/CMakeLists.txt file, which guides the compilation process for the main component, is as follows:

idf_component_register(SRCS "blink.c" INCLUDE_DIRS "." )

Among them, main/CMakeLists.txt only calls one compilation system function, that is idf_component_register. Similar to the CMakeLists.txt for most other components, blink.c is added to SRCS, and the source files added to SRCS will be compiled. At the same time, ".", which represents the path where CMakeLists.txt is located, should be added to INCLUDE_DIRS as the search directories for header files. The content of CMakeLists.txt is as follows:

# Specify v3.5 as the oldest CMake version supported by the current project
# Versions lower than v3.5 must be upgraded before compilation continues
cmake_minimum_required(VERSION 3.5)
# Include the default CMake configuration of the ESP-IDF compilation system
# Create a project named "blink"

Among them, the CMakeLists.txt in the root directory mainly includes $ENV{IDF_PATH}/tools/cmake/project.cmake, which is the main CMake configuration file provided by ESP-IDF. It is used to configure the default rules of the ESP-IDF compilation system and define common functions such as idf_component_register; project(blink) creates a project called blink, and the final firmware will be named blink.bin.