티스토리 뷰
안녕하세요. 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
- Arduino
- ESP-IDF
- 아두이노
- 3D 프린터
- fusion360
- 리눅스
- 3D Printer
- 자작
- C언어
- esp32
- 프린터
- 3D
- Hypercube
- 오픈소스
- 라즈베리파이
- 오픈소스 하드웨어
- 코딩테스트
- ESP
- SQLITE3
- 설계
- DIY
- Fusion 360
- IOT
- 프로젝트
- 쏘카
- 하드웨어
- 해커랭크
- 퓨전360
- C++
- 3d프린터
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |