Picoruby를 ESP32로 포팅하는 방법

[JA] Porting PicoRuby to Another Microcontroller: ESP32 / Yuhei Okazaki @Y_uuu

작성자
RubyKaigi
발행일
2025년 05월 27일

핵심 요약

  • 1 Picoruby는 마이크로컨트롤러에서 Ruby를 실행할 수 있게 하는 경량화된 Ruby 구현체입니다.
  • 2 ESP32로 Picoruby를 포팅하는 과정은 소스 코드 빌드, 라이브러리 링크, 표준 입출력 및 기타 젬 포팅의 4단계로 진행됩니다.
  • 3 포팅 과정에서 워치독 타이머, `require` 문제, 스택 오버플로우, CSI 명령어 오류 등 다양한 기술적 난관이 발생했으나 성공적으로 해결되었으며, 실제 ESP32 보드에서 Ruby 프로그램 실행 데모가 시연되었습니다.

도입

본 세션은 마이크로컨트롤러 환경에 최적화된 경량화된 Ruby 구현체인 Picoruby를 Espressif Systems의 저전력, 고성능 마이크로컨트롤러인 ESP32로 포팅하는 복잡한 과정과 그 기술적 해결책을 상세히 다룹니다. 발표자인 오카자키는 피오르드 부트캠프 멘터이자 IoT 클라우드 엔지니어로서, Raspberry Pi Pico와 같은 마이크로컨트롤러 보드에서 Ruby를 구동할 수 있는 Picoruby의 잠재력과 현재 포팅 현황을 소개하며, 특히 ESP32가 Wi-Fi 및 Bluetooth 기능을 내장하여 IoT 애플리케이션에 적합하다는 점을 강조합니다. 이 발표는 단순히 포팅 과정을 설명하는 것을 넘어, 실제 개발 과정에서 직면할 수 있는 다양한 기술적 난관과 이를 극복한 경험을 공유함으로써, 임베디드 시스템에서 Ruby를 활용하고자 하는 개발자들에게 실질적인 통찰을 제공합니다. 사용된 두 가지 개발 보드, 즉 M5Stack C3M (RISC-V 아키텍처)과 ESP32 DevKitC (Xtensa 아키텍처)는 다양한 CPU 아키텍처에 대한 포팅의 필요성과 복잡성을 시사하며, 발표의 주요 주제인 포팅 작업의 배경을 명확히 합니다.

Picoruby를 ESP32로 포팅하는 과정은 크게 네 가지 단계로 진행됩니다. 첫째, Picoruby 소스 코드를 빌드하여 libmrb.a 라이브러리를 생성하는 단계입니다. 이 과정에서는 C 언어의 컴파일, 링크, 아카이브 개념 및 크로스 컴파일의 중요성이 설명됩니다. ESP-IDF 개발 환경을 사용하여 ESP32 전용 크로스 컴파일러를 활용하며, build_config 파일을 Ruby로 작성하여 컴파일러 및 링커 설정을 정의합니다. 둘째, 생성된 libmrb.a를 ESP32 프로젝트에 링크하여 전체 빌드를 완료하는 단계입니다. ESP-IDF 기반의 프로젝트를 생성하고, main.c 파일에서 Picoruby 초기화 및 VM 실행 로직을 구현합니다. 특히 Ruby 프로그램을 Picoruby VM이 해석할 수 있는 중간 언어(C 파일 형태)로 변환하고 이를 main.c에 포함하는 방식이 설명됩니다. CMakeLists.txt 파일을 통해 libmrb.a의 경로를 지정하고 링크 설정을 완료하여 최종 바이너리 파일을 생성합니다. 셋째, 표준 입출력 기능을 구현하는 단계입니다. Ruby의 puts 메서드 호출 경로를 추적하여 picoruby-machine 젬 내의 hal_write 함수가 호출됨을 확인합니다. build_configpicoruby-machine 젬을 추가하고, ESP32 환경에 맞는 hal_write 함수를 ports/ESP32 디렉토리에 새로 구현합니다. 초기 printf만을 사용한 구현의 문제점(n 바이트 무시, 버퍼링)을 파악하고, putcharfflush를 활용하여 올바른 시리얼 출력을 구현하는 과정이 중요하게 다루어집니다. 넷째, 다른 MRB 젬들을 ESP32 환경에 맞게 포팅하는 단계입니다. 목표는 ESP32에서 셸을 구동하고 echo 명령어를 실행하는 것으로 설정되었습니다. 이를 위해 필요한 젬들을 분석하고, picoruby-io, picoruby-console, picoruby-filesystem-fat 등 핵심 젬들의 포팅 필요성을 식별합니다. 특히 picoruby-filesystem-fat와 같이 복잡한 젬의 경우, 초기에는 포팅을 생략하고 빈 함수로 링크 오류를 회피하는 전략을 사용했으나, 최종적으로는 모든 필요한 젬이 포팅되어 완전한 기능을 제공하게 됩니다.

이러한 포팅 과정에서 다양한 기술적 난관이 발생했습니다. 주요 문제점으로는 첫째, 워치독 타이머 오류입니다. ESP32의 RTOS(FreeRTOS)는 특정 태스크가 일정 시간 이상 CPU를 점유하면 워치독 타이머 오류를 발생시키는데, Picoruby VM의 독자적인 태스크 전환(TCB) 메커니즘이 RTOS와 충돌하여 발생했습니다. 해결책은 워치독 타이머를 비활성화하고 Picoruby VM에 컨텍스트 전환을 전적으로 위임하는 것이었습니다. 둘째, require 명령어 동작 오류입니다. picoruby-shellpicoruby-require 젬이 빌드 시 동적으로 C 언어 소스 코드를 생성하는데, 이 생성된 함수를 main 함수에서 호출하지 않아 발생했습니다. 해결책은 생성된 함수를 main 함수에서 명시적으로 호출하는 것이었습니다. 셋째, 스택 오버플로우입니다. ESP32의 app_main 태스크에 할당된 기본 스택 크기(3584바이트)가 복잡한 Ruby 프로그램 실행에 부족하여 발생했습니다. 해결책은 sdkconfig.defaults 파일에서 스택 크기를 8KB로 늘리는 것이었습니다. 넷째, CSI 명령어 동작 오류입니다. 셸 시작 시 터미널 에뮬레이터 상태 확인을 위해 사용되는 CSI 명령어(CSI DSR)가 올바르게 처리되지 않아 로고가 표시되지 않는 문제였습니다. 이는 hal_write 함수의 초기 구현이 출력 바이트 수를 무시하고 버퍼링 문제를 야기했기 때문이며, putcharfflush를 활용한 정확한 구현으로 해결되었습니다.

이러한 모든 난관을 극복한 후, 발표자는 ESP32 DevKitC 보드에 자작 기판을 연결하여 성공적인 데모를 시연했습니다. 데모에서는 echo 명령어 실행, IRB(Interactive Ruby)를 통한 배열 및 문자열 조작, pwd, ls와 같은 파일 시스템 명령어 실행, 그리고 궁극적으로 Picoruby로 구현된 벽돌 깨기 게임이 매트릭스 LED, 조이스틱, 압전 부저와 연동되어 구동되는 모습을 보여주었습니다. 이는 picoruby-spi, picoruby-adc, picoruby-pwm 등 다양한 Picoruby 젬들이 ESP32 환경에 성공적으로 포팅되었음을 입증하는 결과였습니다.

결론

결론적으로, 본 세션은 Picoruby를 ESP32 마이크로컨트롤러로 포팅하는 전반적인 과정과 그 과정에서 발생할 수 있는 주요 기술적 문제점들을 심층적으로 분석하고 해결 방안을 제시했습니다. 이 포팅 방법론은 ESP32뿐만 아니라 STM32, NRF52와 같은 다른 마이크로컨트롤러에도 확장 적용될 수 있음을 강조하며, 임베디드 시스템 개발 분야에서 Ruby의 활용 가능성을 넓히는 데 기여할 수 있음을 시사합니다. 발표자는 Picoruby 포팅에 대한 추가적인 기여와 협력을 독려하며, 관련 코드와 자세한 구현 사항은 GitHub 저장소에서 확인할 수 있음을 안내합니다. 이는 Picoruby 커뮤니티의 활성화와 더 많은 개발자들의 참여를 유도하여, 마이크로컨트롤러 환경에서 Ruby 생태계를 더욱 풍성하게 만들고자 하는 비전을 보여줍니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!