티스토리 뷰

반응형

안녕하세요. 09LABS 입니다.
저번 강의에서는 개발 환경을 구축하는 방법에 대해 알아봤습니다.
이번 장에서는 빌드 시스템을 구축하는 방법에 대해 알아보겠습니다.

ESP-IDF 프로젝트는 여러 구성요소가 융합된 형태를 사용하고 있습니다.
예를 들어 온습도 센서를 사용한 웹서버의 경우 아래와 같은 Component들이 사용됩니다.

- Wi-Fi 드라이버
- TCP/IP Stack
- FreeRTOS 운영체제
- Web Server
- Sensor Driver
- Main code

ESP-IDF에서 프로젝트를 컴파일하기 위한 빌드시스템은 프로젝트 디렉토리, 구성요소 디렉토리를 지정해야 합니다.
이를 기반으로 빌드되며 빌드 순서는 Component 빌드 -> Project 빌드 순서로 진행됩니다.

Build system 구성
idf.py 에는 프로젝트 빌드를 쉽게 관리할 수 있도록 아래와 같이 세 가지 툴을 사용하여 프로젝트를 관리합니다.

- CMake
- ninja
- esptool.py

CMake는 프로젝트를 구성하고 최종 빌드 툴인 ninja와 함께 사용되는 빌드 파일을 생성합니다.
이전 강의에서 프로젝트를 빌드하기 위해서 타겟을 설정하고 아래와 같이 명령어를 입력했습니다.

$ idf.py build

idf.py build 명령어에는 아래의 명령어가 압축되어있다고 볼 수 있습니다.

$ mkdir -p build
$ cd build
$ cmake .. -G Ninja
$ ninja

1. build 디렉토리를 생성합니다.
2. build 디렉토리로 이동합니다.
3. cmake를 사용하여 Ninja 빌드 파일을 생성합니다.
4. ninja를 사용하여 최종 빌드를 실행합니다.

실제로 프로젝트 디렉토리에서 위와 같이 입력하면 프로젝트를 빌드할 수 있습니다.
아래에서 빌드를 실제로 진행해보겠습니다.

# esp-idf 디렉토리로 이동
$ cd $HOME/esp/esp-idf

# export.sh를 실행하여 환경변수 설정
$ . ./export.sh

# 예제 프로젝트 디렉토리로 이동
$ cd examples/get-started/hello_world

# Target board 설정
$ idf.py set-target esp32

# build 디렉토리 생성 후 이동
$ mkdir build && cd build

# cmake를 사용하여 Ninja 빌드파일 생성
$ cmake .. -G Ninja

# ninja를 실행하여 프로젝트 빌드
$ ninja

# ninja를 사용하여 프로젝트 플래시
$ ninja flash

예제 프로젝트 구성
지금까지 ESP-IDF의 빌드 시스템의 구성과 빌드 방법에  대해 알아봤습니다.
이제 예제 프로젝트를 직접 생성하여 프로젝트를 구성하는 방법에 대해 알아보겠습니다.
기본적으로 프로젝트의 디렉토리 트리 구조는 아래와 같습니다.

- myProject/
             - CMakeLists.txt
             - sdkconfig
             - bootloader_components/ - boot_component/ - CMakeLists.txt
                                                        - Kconfig
                                                        - src1.c
             - components/ - component1/ - CMakeLists.txt
                                         - Kconfig
                                         - src1.c
                           - component2/ - CMakeLists.txt
                                         - Kconfig
                                         - src1.c
                                         - include/ - component2.h
             - main/       - CMakeLists.txt
                           - src1.c
                           - src2.c

             - build/

위 프로젝트 디렉토리에는 아래와 같은 구성요소가 포함되어 있습니다.

- CMakeLists.txt : 최상위 디렉토리와 각 Component, Main code에는 CMakeLists.txt 파일이 포함되어 있습니다. CMake가 프로젝트 빌드를 하기 위해 사용되며 빌드 시스템이 어떻게 구성되어 있는지 기술되어 있습니다.
- sdkconfig : sdkconfig는 프로젝트 구성 파일이며 idf.py menuconfig 명령어에 의해 생성됩니다. 필수로 포함되어야 하는 파일은 아닙니다.
- bootloader_components : 부트로더에 필요한 구성요소가 포함되어 있으며 필수는 아닙니다. 부트로더를 수정해야 하는 경우 사용됩니다.
- components : 프로젝트에 필요한 구성요소들이 포함되어 있으며 필수는 아닙니다. 예를 들어 프로젝트에 센서 드라이버를 포함하고 싶은 경우 components 디렉토리에 각 구성요소 디렉토리를 생성 후 CMakeLists.txt와 소스코드, 헤더파일을 포함시킵니다.
- main : main 디렉토리는 프로젝트 중심이 되는 소스코드가 포함된 디렉토리 입니다. 따라서 프로젝트에 소스코드가 많이 있는 경우 main 디렉토리에 포함시키기 보다는 기능별로 components 디렉토리에 구성요소 생성하는 것이 좋습니다.
- build : idf.py로 프로젝트를 빌드하는 경우 자동으로 생성되는 디렉토리이며 임시로 빌드 파일이 생성되는 곳입니다. 최종 바이너리 파일도 이 디렉토리에 생성됩니다.


예제 프로젝트 생성 및 빌드
위에서 프로젝트 디렉토리에 포함되어야 하는 구성요소에 대해 알아봤습니다.
이제 간단하게 1초에 한 번씩 숫자를 0부터 10까지 1씩 증가시키며 출력하는 예제를 생성해보겠습니다.
먼저 프로젝트 디렉토리를 생성하고 기본 구성요소를 포함시켜 보겠습니다.

# 예제 프로젝트 디렉토리 생성
$ cd $HOME/esp/ && mkdir count_number

# 예제 프로젝트 디렉토리 이동 및 구성요소 생성
$ cd count_number
$ mkdir main
$ touch CMakeLists.txt
$ touch main/CMakeLists.txt
$ touch main/count_number.c

위와 같이 실행을 하면 기본적으로 생성해야 하는 구성요소가 생성되며 트리 구조는 아래와 같습니다.

├── CMakeLists.txt
└── main
    ├── CMakeLists.txt
    └── count_number.c

이제 최상위 CMakeLists.txt를 작성해 보겠습니다.

최상위 CMakeLists.txt 최소 구성요소

# 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.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(count_number)

최상위 CMakeLists.txt에 포함되어야하는 내용은 다음과 같습니다.

- cmake_minimum_required(VERSION 3.16) : 프로젝트 빌드에 필요한 최소 CMake 버전에 대한 정보입니다. ESP-IDF는 CMake 3.16 이상에서 작동하도록 설계되었기 때문에 위와 같이 작성합니다. 이 줄은 최상위 CMakeLists.txt 파일에서 가장 첫 번째 줄에 위치해야 합니다.
- include($ENV{IDF_PATH}/tools/cmake/project.cmake) : 나머지 CMake 기능을 가져와서 프로젝트를 구성하고 모든 Components를 검색하는 등의 작업을 수행합니다.
- project(count_number) : 프로젝트를 생성하고 이름을 지정합니다. 프로젝트의 이름은 최종 바이너리의 이름으로 사용됩니다. (Ex : count_nubmer.bin, count_number.elf). CMakeLists 파일 하나당 하나의 프로젝트만 정의할 수 있습니다.

위 세 가지 내용은 최상위 CMakeLists.txt에 포함되어야하는 최소 구성요소이니 하나도 빠짐없이 작성해야 합니다.

main CMakeLists.txt 작성
main 디렉토리에 생성한 CMakeLists.txt에는 아래와 같이 작성합니다.

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

main CMakeLists.txt에는 빌드하고자 하는 소스코드, 참조할 헤더파일이 위치한 디렉토리를 정의할 수 있습니다.
만약 소스코드가 여러 개라면 다음과 같이 작성합니다.

idf_component_register(SRCS "src1.c" "src2.c" "src3.c"
                    INCLUDE_DIRS "")

main 소스코드 작성
저는 프로젝트 이름을 count_number로 작성했기 때문에 main 소스코드 파일명도 count_number.c로 생성했습니다.
0부터 10까지 1초에 1씩 증가시키는 소스코드는 아래와 같습니다.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

int count = 0;

void app_main(void) {
    while(1) {
        printf("count : %d\n", count);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        count++;
        if (count > 10) {
            count = 0;
        }
    }
}

기본적으로 main 코드에는 app_main이라는 함수가 존재하며 이는 C언어에서 main() 함수와 동일한 기능을 수행합니다.
ESP32는 FreeRTOS를 기반으로 구동되기 때문에 stdio.h 뿐만 아니라 두 가지 헤더를 포함시켰습니다.

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

ESP-IDF에는 ESP 보드에 최적화된 FreeRTOS가 포함되어 있습니다.
우리가 흔히 아는 delay 함수는 ESP에서 사용되지 않기 때문에 전용 함수를 사용해야 합니다.
vTaskDelay 함수는 freertos/task.h에 포함되어 있습니다.

app_main 함수는 메인 함수이기 때문에 따로 태스크를 생성하지 않거나 while loop를 생성하지 않으면 프로그램이 종료됩니다. 따라서 while loop를 생성하여 무한 루프를 구동시키고 1초에 1씩 숫자를 증가시키도록 소스코드를 작성하였습니다.

while(1) {
    printf("count : %d\n", count);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    count++;

	if (count > 10) {
        count = 0;
    }
}


count_number 프로젝트 빌드
이제 소스코드 작성, CMakeLists.txt 작성이 완료되었으므로 프로젝트를 빌드 후 시리얼 모니터를 실행해보겠습니다.

# 예제 프로젝트 디렉토리로 이동
$ cd $HOME/esp/count_number

# 프로젝트 타겟 설정
$ idf.py set-target esp32

# 프로젝트 빌드
$ idf.py build

# 바이너리 플래시
$ idf.py flash

# 시리얼 모니터 실행
$ idf.py monitor

정상적으로 빌드 후 시리얼 모니터가 실행되면 다음과 같이 표시됩니다.

I (0) cpu_start: App cpu up.
I (187) cpu_start: Pro cpu start user code
I (187) cpu_start: cpu freq: 160000000 Hz
I (187) cpu_start: Application information:
I (190) cpu_start: Project name:     count_number
I (195) cpu_start: App version:      1
I (200) cpu_start: Compile time:     Aug 15 2023 01:37:07
I (206) cpu_start: ELF file SHA256:  f96eb6166a302a91...
I (212) cpu_start: ESP-IDF:          v5.1-dirty
I (217) cpu_start: Min chip rev:     v0.0
I (222) cpu_start: Max chip rev:     v0.99 
I (227) cpu_start: Chip rev:         v0.1
I (231) heap_init: Initializing. RAM available for dynamic allocation:
I (239) heap_init: At 3FC94448 len 000552C8 (340 KiB): DRAM
I (245) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM
I (251) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (258) heap_init: At 600FE010 len 00001FF0 (7 KiB): RTCRAM
I (265) spi_flash: detected chip: gd
I (268) spi_flash: flash io: dio
W (272) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (285) sleep: Configure to isolate all GPIO pins in sleep state
I (292) sleep: Enable automatic switching of GPIO sleep configuration
I (299) app_start: Starting scheduler on CPU0
I (304) app_start: Starting scheduler on CPU1
I (304) main_task: Started on CPU0
I (314) main_task: Calling app_main()
count : 0
count : 1
count : 2
count : 3
count : 4
count : 5
count : 6
count : 7
count : 8
count : 9
count : 10
count : 0
count : 1
count : 2
count : 3
count : 4

빌드한 소스코드가 정상적으로 플래싱 되었고 숫자가 0부터 10까지 증가하는 것을 확인할 수 있습니다.


ESP-IDF 기반의 프로젝트 빌드시스템의 구성요소에 대해 알아보고 예제 프로젝트를 빌드 & 실행해보았습니다.
아두이노와 달리 프로젝트를 생성하는 부분이 까다롭다고 느껴질 수 있지만 내가 원하는 대로 프로젝트를 구성할 수 있기 때문에 더욱 심도있는 기능을 포함시킬 수 있을 것 같습니다.
다음 강의에서는 ESP-IDF의 멀티태스킹과 유사한 Task 생성 방법에 대해 알아보겠습니다.

반응형

'코딩하자 > ESP-IDF' 카테고리의 다른 글

[ESP-IDF] 3. Task API (feat. 멀티태스킹)  (0) 2024.02.19
[ESP-IDF] 1. ESP32 개발환경 구축  (0) 2023.08.15
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함