서론
이전 글에서는 STM32 기반의 Custom Board에 Zephyr RTOS를 포팅하고, LED, UART, Thread까지 기본적인 실습을 진행했습니다. 이를 통해 Zephyr의 개발 환경과 Device Tree, 그리고 Zephyr API를 사용하는 방법을 익혀 보았습니다.
이번에는 플랫폼을 ESP32 CAN Board로 옮겨 Zephyr를 포팅하고 실습을 진행해 보겠습니다.
ESP32는 Wi-Fi와 Bluetooth를 기본으로 지원하며 가격도 저렴하여 IoT 프로젝트에서 널리 사용되는 MCU입니다. 특히 CAN 기능이 포함된 ESP32 CAN Board는 산업용 통신과 IoT를 함께 실습하기에 적합한 플랫폼입니다.
다만 이 보드는 Zephyr에서 그대로 사용할 수 있는 공식 지원 보드가 아니므로, 보드에 맞는 포팅 작업이 필요합니다. 이번 글에서는 ESP32 CAN Board에 필요한 보드 포팅을 수행하고, RGB LED를 제어하여 Zephyr가 정상적으로 동작하는지 확인해 보겠습니다.
앞으로 이어질 시리즈에서는 이번에 포팅한 보드를 기반으로 GPIO, UART, CAN, Wi-Fi 등 ESP32의 다양한 기능을 Zephyr API를 이용하여 하나씩 실습해 볼 예정입니다.
ESP32 CAN 보드 소개
이번 실습에 사용할 보드는 SK Pang Electronics의 ESP32 CAN-BUS Board입니다. ESP32 WROOM-32 모듈과 CAN 트랜시버를 하나의 보드에 통합한 제품으로, USB-C를 이용한 프로그램 다운로드와 CAN 통신을 모두 지원합니다.
또한 Wi-Fi와 Bluetooth가 기본 내장되어 있어 IoT 애플리케이션을 개발하기에 적합하며, CAN 인터페이스를 함께 제공하므로 산업용 통신 실습에도 활용할 수 있습니다.
앞으로 이 시리즈에서는 이 보드를 기반으로 GPIO, UART, CAN, Wi-Fi 등 ESP32의 다양한 기능을 Zephyr RTOS를 이용하여 단계적으로 실습해 볼 예정입니다.
아래는 이번 실습에 사용할 ESP32 CAN Board의 모습입니다.

ESP32 CAN Board의 주요 특징은 다음과 같습니다.
- ESP32 WROOM-32 (Dual-Core Tensilica LX6, 최대 240 MHz)
- Wi-Fi (802.11 b/g/n)
- Bluetooth Classic 및 BLE
- 520 KB SRAM
- 4 MB Flash
- CAN Transceiver 내장
- USB Type-C 프로그래밍 지원
- RGB LED 내장
- 역극성 보호 기능을 갖춘 외부 전원 입력
아래 링크에서 보드의 자세한 사양을 확인할 수 있습니다. 또한 회로도(Schematic)와 예제 코드(Demo Code)도 함께 다운로드할 수 있습니다.
이제 ESP32 CAN Board의 특징을 살펴보았으므로, 다음으로 Zephyr에서 사용할 수 있도록 Board 포팅을 진행해 보겠습니다.
Board 포팅
이전 글의 STM32 Zephyr 포팅에서는 필요한 파일을 하나씩 직접 작성하면서 Custom Board를 구성했습니다. 그러나 이번에 사용하는 ESP32 CAN Board는 ESP32 WROOM-32 모듈을 기반으로 설계된 보드이므로, Zephyr에서 공식 지원하는 esp32_devkitc 보드를 기반으로 필요한 부분만 수정하여 새로운 Board를 작성하겠습니다.
즉, 처음부터 모든 Board 파일을 새로 만드는 것이 아니라, esp32_devkitc의 Board 정의를 참고하여 ESP32 CAN Board에 맞게 수정하는 방식으로 포팅을 진행합니다. 이렇게 하면 Zephyr에서 이미 검증된 설정을 그대로 활용할 수 있어 포팅 과정을 보다 쉽고 안정적으로 진행할 수 있습니다.
참고하는 Board는 esp32_devkitc입니다.
Zephyr는 다양한 개발 보드를 공식적으로 지원하고 있으므로, 하드웨어 구성이 유사한 보드가 있다면 이를 기반으로 새로운 Board를 만드는 것이 일반적인 포팅 방법입니다.
esp32_devkitc 보드 확인 및 새 보드 폴더 생성
먼저 Zephyr에서 공식적으로 지원하는 esp32_devkitc 보드의 위치를 확인해 보겠습니다.
보드 파일은 다음 경로에 위치합니다.
zephyr
└── boards
└── espressif
└── esp32_devkitc
이 디렉터리에는 Board를 구성하는 데 필요한 DTS(Device Tree), Kconfig, CMake 설정 파일 등이 포함되어 있습니다.
아래는 esp32_devkitc 보드의 디렉터리 구조입니다.

이제 우리가 사용할 Board에 대한 정보를 저장할 디렉터리를 만들 차례입니다.
Zephyr에서 제공하는 esp32_devkitc 보드를 직접 수정하지 않고, 사용자 Board로 관리하기 위해 별도의 Board를 생성하겠습니다. 이렇게 하면 Zephyr를 업데이트하더라도 직접 작성한 Board가 영향을 받지 않아 유지보수가 편리합니다.
이를 위해 my_boards/boards/skpang 디렉터리를 생성한 후, esp32_devkitc 디렉터리를 복사하여 skpang_esp32_can으로 이름을 변경합니다.
디렉터리 구조는 다음과 같습니다.

이후부터는 skpang_esp32_can 디렉터리에서 필요한 파일을 하나씩 수정하여 ESP32 CAN Board에 맞는 Board를 완성해 보겠습니다.
Board 정보 수정
esp32_devkitc 폴더에는 아래와 같은 파일이 들어 있습니다.

우리가 만든 skpang_esp32_can 폴더에도 동일한 내용이 들어 있는데 esp32_devkitc라고 되어 있는 부분을 전부 skpang_esp32_can으로 교체 합니다.
아래는 교체가 완료된 폴더 내용입니다.’

현재는 파일 이름과 디렉터리 이름만 변경한 상태이며, 각 파일의 내용은 아직 기존 esp32_devkitc 보드 정보를 그대로 사용하고 있습니다.
따라서 이제부터는 각 설정 파일을 하나씩 수정하여 ESP32 CAN Board에 맞는 새로운 Board 정보를 작성하겠습니다.
board.yml 수정
가장 먼저 board.yml 파일을 수정하겠습니다.
이 파일은 Board의 이름, Vendor, 지원하는 SoC 등 Board에 대한 기본 정보를 정의하는 파일입니다.
다음과 같이 board.yml을 수정합니다.

Kconfig 수정
다음으로 Kconfig 관련 파일을 수정하겠습니다.
Kconfig는 Zephyr에서 Board와 관련된 설정 항목 및 기본 구성을 정의하는 역할을 합니다. 새로운 Board를 추가하는 경우에는 해당 Board를 위한 Kconfig 항목을 함께 정의해야 합니다.
새로운 Board를 추가하는 경우에는 기존 esp32_devkitc에 대한 설정을 skpang_esp32_can으로 변경해야 합니다.
이번 실습에서는 다음 두 개의 파일을 수정합니다.
KconfigKconfig.skpang_esp32_can
Kconfig
Kconfig 파일은 현재 Board에서 사용할 Kconfig 설정 파일을 포함(include)하는 역할을 합니다. 따라서 파일 이름이 변경되었다면 이 부분도 함께 수정해야 합니다.
아래와 같이 수정합니다.

Kconfig.skpang_esp32_can
다음으로 Kconfig.skpang_esp32_can 파일을 수정합니다.
Kconfig.skpang_esp32_can 파일에는 Board 이름과 사용할 ESP32 SoC에 대한 설정이 정의되어 있습니다. Board 이름이 변경되었으므로 관련 설정도 새로운 Board 이름에 맞게 수정합니다.
아래와 같이 수정합니다.

수정이 완료되면 Zephyr는 기존 esp32_devkitc 대신 skpang_esp32_can을 하나의 독립된 Board로 인식하게 됩니다. 이제 Board의 기본 설정이 완료되었으므로, 다음으로 YAML 파일을 수정하여 Board의 지원 정보를 정의하겠습니다.
YAML 파일 수정
다음으로 YAML 파일을 수정하겠습니다.
YAML 파일은 Board가 지원하는 아키텍처와 SoC, Toolchain, RAM, Flash 등의 정보를 정의하는 파일입니다. 또한 west boards 명령이나 Visual Studio Code에서 Board를 검색할 때도 이 정보를 사용합니다.
이번 실습에서는 다음 두 개의 파일을 수정합니다.
skpang_esp32_can_appcpu.yamlskpang_esp32_can_procpu.yaml
두 파일 모두 기존 esp32_devkitc의 내용을 기반으로 하고 있으므로, Board 이름을 새로운 skpang_esp32_can에 맞게 수정합니다.
skpang_esp32_can_appcpu.yaml 수정
먼저 skpang_esp32_can_appcpu.yaml 파일을 수정합니다.
이 파일에는 Application CPU(App CPU)를 사용하는 Board 정보가 정의되어 있습니다.
아래와 같이 Board 이름을 skpang_esp32_can_appcpu로 변경합니다.

skpang_esp32_can_procpu.yaml 수정
다음으로 skpang_esp32_can_procpu.yaml 파일을 수정합니다.
이 파일은 ESP32의 Primary CPU(Pro CPU)를 사용하는 Board 정보를 정의합니다.
마찬가지로 Board 이름을 skpang_esp32_can_procpu로 변경합니다.

지금까지는 새로운 Board를 Zephyr에 등록하는 작업을 수행했습니다. 다음으로 Device Tree(DTS)를 수정하여 ESP32 CAN Board의 실제 하드웨어 구성을 반영해 보겠습니다.
Device Tree(DTS) 수정
다음으로 Device Tree(DTS) 파일을 수정하겠습니다.
Device Tree는 Board에 연결된 하드웨어를 Zephyr에 알려주는 역할을 합니다. MCU에 어떤 GPIO가 연결되어 있는지, 어떤 LED와 UART를 사용하는지 등의 하드웨어 정보를 모두 이 파일에서 정의합니다.
이번 실습에서는 기존 esp32_devkitc의 Device Tree를 기반으로 ESP32 CAN Board의 하드웨어 구성에 맞게 수정하겠습니다.
ESP32는 Dual-Core MCU이므로 appcpu와 procpu용 DTS 파일이 각각 제공됩니다. 이번 실습에서는 Pro CPU를 대상으로 동작하는 skpang_esp32_can_procpu.dts 파일만 수정하겠습니다.
먼저 RGB LED의 연결 상태를 회로도를 통해 확인하고 이를 Device Tree에 반영하겠습니다.
아래는 RGB Led가 어느 핀에 연결되는지를 나타내는 회로도 입니다.

회로도를 보면 아래와 같이 RGB Led는 ESP-WROOM-32 Chip의 각 포트에 연결이 되어 있습니다.
- LED_R: IO2
- LED_G: IO15
- LED_B: IO4
확인한 GPIO 정보를 Device Tree에 등록하여 Zephyr가 RGB LED를 제어할 수 있도록 설정합니다.

Device Tree의 aliases 노드는 주요 하드웨어에 별칭을 부여하는 역할을 합니다. Application에서는 led0, led1, led2와 같은 이름으로 장치를 쉽게 참조할 수 있으므로, RGB LED를 각각 아래와 같이 등록합니다.

이렇게 Device Tree를 수정하면 ESP32 CAN Board의 RGB LED가 Zephyr에 등록됩니다.
다음으로 CAN 인터페이스를 Device Tree에 등록하여 ESP32 CAN Board의 하드웨어 구성을 계속 완성해 보겠습니다.
아래는 회로도를 보면 CAN_TD는 IO25, CAN_RD는 IO26에 연결된 것을 알 수 있습니다.

<참고>
회로도에서는 CAN_TD(CAN_TX)와 CAN_RD(CAN_RX)로 표기되어 있습니다.
이후부터는 Zephyr와 ESP-IDF의 표기에 맞추어 CAN_TX와 CAN_RX를 사용하겠습니다.
Zephyr에서는 핀 설정(Pin Multiplexing)과 장치(Device) 설정을 분리하여 관리합니다. 따라서 CAN 인터페이스를 사용하려면 pinctrl.dtsi에서 CAN 핀을 정의하고, .dts에서 CAN 장치를 활성화해야 합니다.
먼저 skpang_esp32_can-pinctrl.dtsi 파일에서 CAN에 사용할 GPIO를 등록합니다. ESP32에서는 CAN 드라이버가 TWAI(Two-Wire Automotive Interface)라는 이름으로 제공되므로, CAN 관련 핀도 twai라는 이름으로 정의되어 있습니다.

다음으로 skpang_esp32_can_procpu.dts 파일에서 TWAI 장치를 활성화하고, 앞에서 정의한 twai_default 핀 설정을 연결합니다.

이렇게 설정하면 GPIO25는 CAN 송신(TX), GPIO26은 CAN 수신(RX) 핀으로 동작하며, Zephyr에서 CAN(TWAI) 드라이버를 사용할 수 있게 됩니다.
Board 포팅 정리
지금까지 새로운 skpang_esp32_can Board를 생성하고, Board 정보와 Device Tree를 수정하여 ESP32 CAN Board에 맞는 하드웨어 구성을 완료했습니다.
주요 변경 사항은 다음과 같습니다.
- 새로운 Board 생성
board.yml수정- Kconfig 수정
- YAML 수정
- RGB LED 등록
- CAN(TWAI) 등록
이제 Zephyr는 skpang_esp32_can을 하나의 독립된 Board로 인식하며, RGB LED와 CAN 인터페이스를 사용할 수 있는 기본 환경이 준비되었습니다.
마지막으로 Zephyr가 새로 추가한 Board를 정상적으로 인식하는지 확인해 보겠습니다.
다음 명령을 실행하면 사용자 Board가 정상적으로 등록되었는지 확인할 수 있습니다.
west boards --board-root D:\Zephyr\workspace\my_boards | findstr skpang
정상적으로 포팅되었다면 아래와 같이 skpang_esp32_can Board가 표시됩니다.
![]()
이제 Board 포팅이 완료되었습니다. 다음으로 RGB LED Application을 작성하여 새로 생성한 Board가 정상적으로 동작하는지 확인해 보겠습니다.
RGB LED Application 작성
RGB LED를 제어하는 간단한 Application을 작성해 보겠습니다.
이번 실습에서는 RGB LED 중 Red LED를 500 ms 주기로 점멸하도록 하겠습니다. 이를 통해 앞에서 Device Tree에 등록한 RGB LED가 정상적으로 동작하는지 확인할 수 있습니다.
이번 Application에서는 아직 별도의 Thread를 생성하지 않고, main() 함수에서 LED를 직접 제어하는 간단한 구조로 작성하겠습니다.
아래는 main.c 프로그램 입니다.

코드 설명
먼저 gpio_is_ready_dt()를 사용하여 Device Tree에 등록한 RGB LED의 GPIO가 정상적으로 사용 가능한 상태인지 확인합니다.
이후 gpio_pin_configure_dt()를 이용하여 Red, Green, Blue LED의 GPIO를 출력으로 설정하고, 초기 상태는 모두 OFF가 되도록 설정합니다.
gpio_pin_configure_dt(&led_r, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&led_g, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&led_b, GPIO_OUTPUT_INACTIVE);
이 보드의 RGB LED는 Active Low 방식이므로 GPIO_OUTPUT_INACTIVE로 설정하면 실제 GPIO 출력은 High가 되고 LED는 꺼진 상태가 됩니다.
마지막으로 while(1) 루프에서 gpio_pin_toggle_dt()를 이용하여 Red LED의 상태를 반전시키고, k_msleep(500)으로 500 ms 동안 대기합니다.
gpio_pin_toggle_dt(&led_r);
k_msleep(500);
따라서 Red LED는 500 ms마다 ON/OFF 상태가 바뀌면서 반복적으로 점멸하게 됩니다.
이렇게 RGB LED를 제어하기 위한 Application 작성이 완료되었습니다.
이제 앞에서 포팅한 skpang_esp32_can Board를 대상으로 프로젝트를 Build하고, 생성된 Firmware를 ESP32 CAN Board에 Flash하여 실제 동작을 확인해 보겠습니다.
Build 및 Flash
앞에서 작성한 RGB LED Application을 Build하고 ESP32 CAN Board에 Flash하여 실제로 원하는 동작을 하는지 확인해 보겠습니다.
Build를 진행하기 전에 Application을 구성하는 몇 가지 파일을 먼저 작성해야 합니다.
프로젝트 구성 파일 작성
이번 프로젝트에서는 다음 세 파일을 추가하거나 수정하겠습니다.
- CMakeLists.txt : Zephyr Build System에 Application의 소스 파일을 알려주는 파일
- prj.conf : Application에서 사용할 Zephyr 기능과 옵션을 설정하는 파일
- CHANGELOG.md : 프로젝트의 변경 사항과 개발 이력을 기록하는 파일
CMakeLists.txt와 prj.conf는 Zephyr Application을 Build하기 위해 필요한 기본 파일이며, CHANGELOG.md는 Build에 직접 사용되지는 않지만 프로젝트의 변경 이력을 관리하기 위해 함께 작성하겠습니다.
CMakeLists.txt 작성
CMakeLists.txt는 Zephyr Build System에 현재 프로젝트와 Build할 소스 파일을 알려주는 역할을 합니다.
이번 Application에서는 src/main.c만 사용하므로 다음과 같이 작성합니다.

prj.conf 작성
prj.conf는 Application에서 사용할 Zephyr의 기능과 각종 설정 옵션을 지정하는 파일입니다.
이번 RGB LED Application에서는 GPIO를 사용하기 위한 설정과, 프로그램 실행 상태를 확인하기 위한 Serial Console 관련 설정을 추가합니다.

CHANGELOG.md 작성
이 파일은 Zephyr Build에 필요한 파일은 아니지만, 앞으로 기능을 추가하거나 프로그램을 수정할 때 버전별 변경 내용을 기록하기 위해 사용하겠습니다.
내용은 아래와 같습니다.

프로젝트 구성 파일 작성이 완료되었습니다. 이제 앞에서 생성한 skpang_esp32_can Board를 대상으로 실제 Build를 진행해 보겠습니다.
Build
프로그램을 Build 해 보겠습니다.
Build 명령어는 조금전에 작성 했던 CHANGELOG.md 파일의 Build 부분에 있는 내용을 복사하여 Visual Studio Code 의 Terminal에 붙여넣기를 하여 실행하면 됩니다.
명령어는 아래와 같습니다.

위 명령에서는 별도의 Application 디렉터리를 지정하지 않았으므로, 먼저 프로젝트 디렉터리인 skpang_esp32_can_blinky로 이동한 다음 실행해야 합니다.
Build가 성공하면 아래와 같은 메세지가 출력됩니다.

Build가 정상적으로 완료되면 마지막 부분에 다음과 같이 Successfully created ESP32 image. 메시지가 출력됩니다.
또한 prj.conf에서 다음과 같이 Firmware의 이름을 지정했기 때문에,
CONFIG_KERNEL_BIN_NAME="skpang_esp32_can_blinky"
Build 결과로 프로젝트 이름과 동일한 다음 ELF 파일이 생성됩니다.
build/zephyr/skpang_esp32_can_blinky.elf
이것으로 skpang_esp32_can Board를 대상으로 한 Application Build가 정상적으로 완료되었습니다.
다음으로 생성된 Firmware를 ESP32 CAN Board에 Flash해 보겠습니다.
Flash
Flash 명령도 위 md 파일에서 복사하여 사용 할 수 있습니다. 그러나 이 명령은 특별한 옵션이 없고 west flash만 입력하면 되므로 Terminal에서 typing하는 것이 좋습니다.
west flash를 입력하면 아래와 같은 메세지가 출력됩니다.

Flash가 정상적으로 진행되면 west는 연결된 ESP32를 자동으로 검색하고, esptool을 이용하여 Firmware를 Board의 Flash Memory에 기록합니다.
위 결과에서는 ESP32가 COM13으로 정상적으로 인식되었으며, Firmware 기록 후 다음과 같은 메시지를 확인할 수 있습니다.
Hash of data verified.
Hard resetting via RTS pin...
Hash of data verified.는 Flash에 기록된 데이터가 정상적으로 검증되었음을 의미하며, 이후 Board가 자동으로 Reset되면서 새로 기록된 Firmware가 실행됩니다.
따라서 별도의 Programmer나 수동 Reset 과정 없이 다음 명령만으로 Build된 Firmware를 Board에 다운로드하고 실행할 수 있습니다.
실행 결과
Firmware를 Flash한 후 Board가 자동으로 Reset되면서 프로그램이 실행됩니다.
실행 결과, ESP32 CAN Board에 내장된 RGB LED의 Red LED가 500 ms 주기로 정상적으로 점멸하는 것을 확인하였습니다. Green LED와 Blue LED는 초기 설정대로 OFF 상태를 유지합니다.
이를 통해 앞에서 진행한 Device Tree의 RGB LED GPIO 설정과 Application의 GPIO 제어가 정상적으로 동작하고 있음을 확인할 수 있습니다.
또한 Custom Board의 인식부터 Build, Flash 그리고 실제 Firmware 실행까지 전체 과정이 정상적으로 이루어지는 것도 확인하였습니다.
결론
이번 실습에서는 Zephyr에서 공식적으로 지원하는 esp32_devkitc Board를 기반으로 SK Pang의 ESP32 CAN Board에 맞는 새로운 skpang_esp32_can Board를 만들어 보았습니다.
Board를 새로 생성한 후 board.yml, Kconfig, YAML 등의 Board 관련 파일을 수정하였으며, Device Tree에는 실제 회로도를 바탕으로 RGB LED와 CAN(TWAI) 인터페이스를 등록하였습니다.
또한 간단한 RGB LED Application을 작성하고 Build와 Flash를 진행하여 Red LED가 500 ms 주기로 정상적으로 점멸하는 것까지 확인하였습니다.
이번 실습에서 중요한 것은 LED를 점멸시킨 것 자체보다는, 기존 Zephyr Board를 기반으로 실제 하드웨어에 맞는 새로운 Custom Board를 만들고 Application을 실행하는 전체 과정을 직접 확인했다는 점입니다.
이제 ESP32 CAN Board를 Zephyr에서 사용할 수 있는 기본 환경이 준비되었습니다. 다음 실습부터는 이번에 포팅한 Board를 그대로 사용하여 UART, CAN, Wi-Fi 등의 기능을 하나씩 실습해 보겠습니다.