본문으로 건너뛰기
5분 AI.
TRENDSHIFT DAILY #24 딥다이브 · 2026-10-23 분석

xiaoai-patch 딥다이브
— 작은 스피커로 배우는 리눅스 시스템 조립법

펌웨어 빌드 도구

빠른 판단

무엇
XiaoAi 스피커의 루트 파일시스템에 음악·음성비서·기기 제어 프로그램을 통합하는 빌드 레시피 모음
이럴 때
지원 스피커의 시스템 구성을 바꿀 때
입력
기기 또는 펌웨어에서 추출한 원본 파일시스템 이미지
결과
추가 오디오·음성 서비스가 통합된 기기용 파일시스템
샤오미 XiaoAi 스피커의 기존 시스템 이미지를 풀고, 서비스를 바꾸고, 오픈소스 오디오 도구를 넣어 다시 포장하는 프로젝트입니다. 핵심은 거대한 AI 모델보다 Makefile·셸·크로스컴파일·리눅스 서비스가 연결되는 과정에 있습니다. 빌드 레시피부터 Python API, 음성 처리 파이프까지 직접 읽으며 “프로그램 하나”가 “기기에서 동작하는 시스템”이 되는 과정을 공부합니다. 저장소: duhow/xiaoai-patch · 2026-10-23 GitHub 기준 스타 492 · 포크 54 · GPL-3.0 · 커밋 c26bef8 기준.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

기존 스피커의 시스템을 재료로 삼는 임베디드 리눅스 조립 도구

xiaoai-patch는 XiaoAi 스피커의 루트 파일시스템을 수정하고, 음악 재생·음성비서·기기 제어 프로그램을 추가하는 스크립트와 빌드 레시피 모음입니다. 이 저장소에서 배울 핵심은 AI 모델을 만드는 법보다, 여러 프로그램을 작은 리눅스 기기 안에서 함께 동작시키는 법입니다.

재료는 기기에서 얻거나 펌웨어 업데이트에서 추출한 원본 이미지입니다. Makefile의 extract는 이미지를 디렉토리로 풀고, patch는 설정과 파일을 바꿉니다. build는 새 이미지를 만들고, release는 메타데이터·업데이트 도구·선택적인 커널 이미지까지 묶습니다.

추가 프로그램은 별도 빌드 엔진인 packages.sh가 준비합니다. 여기에 음악 서버 MPD, AirPlay 수신 도구 Shairport Sync, 동기 재생 도구 Snapcast, 깨우는 말 감지 도구 Porcupine, Python 웹 API가 연결됩니다. 이들을 이 저장소가 전부 직접 구현한 것은 아닙니다. 외부 소스를 가져와 기기에 맞게 빌드하고 배치하는 연결 코드가 중심입니다.

용어
루트 파일시스템 (rootfs)
리눅스가 사용하는 최상위 경로 / 아래의 파일 묶음입니다. 실행 파일, 공유 라이브러리, 설정, 서비스 시작 스크립트가 들어 있습니다. 커널과는 구분되며, 이 프로젝트의 주된 수정 대상입니다.
한 문장 비유

스피커라는 작은 가게에서, 건물은 두고 주방과 운영 규칙을 바꾸는 일입니다

커널과 하드웨어 드라이버는 전기·수도 설비입니다. MPD와 음성비서는 주방 기구이고, 서비스 스크립트는 개점 순서입니다. packages.sh는 기구를 준비하고, Makefile은 가게 전체를 다시 포장합니다.

설비가 다른 건물에는 같은 기구를 그대로 꽂을 수 없습니다. 모델마다 CPU·라이브러리·플래시 구성이 달라서, 스피커 이름이 비슷하다는 이유만으로 이미지를 공유할 수 없는 것입니다.

1-1. 무엇이 들어오고, 무엇이 나오는가

구분실제 항목읽어 볼 위치
원본 입력기존 root.squashfs, 모델 정보, 필요한 경우 기존 boot.imgMakefile의 FILE·MODEL·LINUX
프로그램 입력버전·다운로드 주소·의존성·빌드 함수packages/*/package.mk
중간 결과build-packages/s2t/armv7, squashfs-rootpackages.sh, scripts/92_copy_build_packages.sh
배포 결과수정된 이미지와 mico_firmware_…tar 묶음Makefile의 build_squashfs·release_pack

research/lx06/install.md는 커널을 수정하거나 다시 빌드하지 않고, 기존 순정 커널 이미지를 함께 배포한다고 명시합니다. packages/kernel-headers가 있다는 사실은 커널 자체를 새로 만든다는 뜻이 아닙니다. 사용자 공간 프로그램을 컴파일할 때 필요한 헤더를 준비하는 작업과 구분해야 합니다.

1-2. 음성비서는 어느 컴퓨터에서 생각할까

스피커에서는 Porcupine이 깨우는 말을 감지하고, 셸 런처가 음성을 녹음합니다. packages/porcupine/config/launcher는 녹음 데이터를 Home Assistant의 STT API로 보냅니다. README와 음성비서 문서에서는 Whisper를 그 제공자 예로 들지만, 이 저장소에 Whisper 모델을 스피커 안에서 추론하는 구현은 없습니다.

런처는 시작 시 확인한 API 실행 상태에 따라, 비어 있지 않은 인식 문장을 로컬 /intent로 먼저 보냅니다. API가 사용 불가로 표시되어 있거나 로컬 조회가 실패하면 Home Assistant의 /api/conversation/process에 보냅니다. 따라서 “로컬 기기를 활용한다”와 “모든 처리가 외부 통신 없이 끝난다”는 서로 다른 조건입니다.

학습의 출발점
하드웨어 없이도 중요한 부분을 공부할 수 있습니다

이 글의 실습은 파일 읽기, 의존성 지도 그리기, 계산과 설계 검토로 구성했습니다. 레포 코드·설치 스크립트를 실행하거나 펌웨어를 기록하지 않아도 시스템 통합의 핵심을 배울 수 있습니다.

2왜 주목받는가

선정 사실과 관심을 끄는 기술적 이유를 구분해서 보기

선정 근거는 2026-10-23의 TrendShift Daily #24입니다. 같은 날 GitHub 기준 스타 492·포크 54입니다. 스타·포크 수와 일일 순위만으로 “어떤 사건 때문에 급상승했다”는 인과관계를 단정할 수는 없습니다.

아래는 프로젝트의 구조를 바탕으로 한 관심을 끌 만한 이유에 대한 해석입니다. 음성비서의 화려한 대화보다, 이미 있는 스피커를 사용자가 선택한 서비스로 다시 구성한다는 점에 이 프로젝트의 매력이 있습니다.

2-1. 하드웨어 재사용과 서비스 선택권

scripts/20_disable_services.sh는 제조사 관련 서비스의 자동 시작 링크를 제거합니다. scripts/24_remove_property_bin.sh는 관련 실행 파일과 라이브러리를 정리합니다. 그 자리에 음악 재생·발견·제어용 프로그램을 넣는 접근입니다.

README는 패치를 조절하면 원래 XiaoAi 기능과 새 프로그램을 함께 유지할 수도 있다고 설명합니다. 그러나 기본 스크립트는 여러 제조사 기능을 제거하도록 작성되어 있습니다. 병행 사용은 자동으로 보장되는 기본 동작이 아니라, 사용자가 수정하고 검증해야 하는 구성입니다.

2-2. 여러 오디오 생태계를 한 기기에 조합한다

구성 요소얻으려는 기능소스에서 확인되는 연결
MPD음악 재생 엔진과 상태 관리packages/mpd/config/mpd.conf의 ALSA·FIFO 출력
Shairport SyncAirPlay 오디오 수신packages/shairport-sync/package.mk와 서비스 설정
Snapcast여러 기기의 동기 오디오 재생packages/snapcast의 서버·클라이언트 빌드
UpmpdcliMPD에 UPnP 계열 제어 창구 추가packages/music/package.mk에서 LX01 외 모델에 포함
Squeezelite별도 음악 서버 생태계의 재생 클라이언트packages/squeezelite/package.mk

여기서 장점은 기존 오픈소스 도구를 작은 장치에 맞춰 결합할 수 있다는 데 있습니다. 각 도구의 설정·리소스·호환성 문제도 함께 관리해야 합니다.

2-3. 대안과 비교하면 선택 기준이 선명해진다

접근장점필요한 대가
제조사 순정 사용원래 앱과 기능을 유지하기 쉬움사용자 정의 기능은 제조사 제공 범위에 좌우됨
별도 보드로 직접 제작부품·운영체제·배치를 직접 선택마이크·앰프·전원·케이스 통합을 직접 설계
xiaoai-patch기존 스피커의 하드웨어와 순정 커널을 활용지원 모델·원본 펌웨어·라이브러리·복구 경로에 제약
소프트웨어만 PC에서 학습기기를 건드리지 않고 구조부터 이해실제 음향·드라이버·부팅 호환성은 확인 불가

이는 개별 상용 제품의 성능 평가가 아니라, 구현 방식의 비교입니다. xiaoai-patch의 장점은 이미 가진 호환 기기를 재활용할 때 커집니다. 새 장치를 구매해야 하거나 안정적인 완제품 경험이 우선이라면 판단이 달라집니다.

2-4. 최근 변경도 시스템 통합의 현실을 보여 준다

HEAD c26bef8은 2026-09-29의 LX06 부팅 반복 문제 문서 수정입니다. 일부 Foresee/ESMT NAND를 예전 커널이 인식하지 못하는 상황을 다룹니다. 최신 푸시가 새 기능 코드라는 뜻은 아니며, 장치별 복구 경험이 문서에 쌓이는 것도 중요한 유지보수입니다.

이 레포의 교육적 가치는 기능 목록보다, 하드웨어 제약이 빌드 옵션과 운영 절차를 어떻게 바꾸는지 드러낸다는 데 있습니다. 다음 절부터는 그 연결을 파일과 함수 단위로 따라갑니다.

3기술 스택 전체 지도

기기 실행부·관리 화면·빌드 호스트를 나눠 읽기

2026-10-23 GitHub 기준 주 언어는 Makefile입니다. 실제로는 최상위 Makefile과 많은 셸 레시피가 조립을 담당합니다. package.mk라는 이름만 보고 모두 GNU Make 문법이라고 생각하면 안 됩니다. packages.sh의 load_package()가 이 파일을 Bash의 source로 읽습니다.

용어
빌드 호스트와 실행 대상
빌드 호스트는 프로그램을 컴파일하는 컴퓨터이고, 실행 대상은 완성된 프로그램이 돌아갈 스피커입니다. 이 저장소에서는 기본적으로 Linux amd64 호스트가 ARM용 결과물을 만듭니다. 호스트용 Python과 스피커용 Python도 구분해야 합니다.

3-1. 백엔드: 기기 제어 API와 음성 처리 연결

항목실제 선언역할·근거 파일
Python3.9.18packages/python3/package.mk에서 기기용 빌드
FlaskFlask>=3api/requirements.txt와 api/main.py의 HTTP API
스케줄러Flask-APScheduler==1.13.1의존성 선언은 있지만 API Python 코드에서 사용하지 않음
HTTP 클라이언트requests>2,<3Home Assistant 상태 조회·인증 토큰 처리
인증서 묶음certifi>=2024요구사항의 하한 범위이며 정확한 설치 버전은 고정되지 않음
음성 관련 의존성wyoming==1.6.0요구사항에 존재; api/main.py의 API와 셸 런처는 HTTP를 사용
설정·버퍼pyyaml, pyring-buffer버전 고정 없음; YAML은 api/intents.py에서 사용

api/requirements.txt의 Flask-MQTT·paho-mqtt·tflite-runtime·numpy는 주석 처리된 항목입니다. 설치되는 의존성처럼 세면 안 됩니다. 특히 numpy는 임시 공간의 가용 메모리를 초과한다는 주석이 있습니다. 실제로 모델 추론을 구현했다는 근거로 삼을 수 없습니다.

api/main.py는 app.run(debug=False, host='0.0.0.0', port=80)로 시작합니다. 별도의 데이터베이스 서버는 보이지 않으며, 설정은 /data/listener와 /data/tts.conf, 시스템 정보는 /proc·/sys·UCI 파일에서 읽습니다.

3-2. 프론트엔드: 빌드 단계가 없는 작은 관리 화면

api/static/index.html은 Materialize 1.0.0을 CDN 주소로 참조합니다. 동작은 api/static/app.js의 순수 JavaScript이며 React·Vue·TypeScript 코드는 없습니다. 저장소 전체에서 package.json·Cargo.toml·pyproject.toml·go.mod도 발견되지 않았습니다.

함수하는 일학습할 연결
initPage()설정을 읽은 뒤 Home Assistant 인증 설정 여부에 따라 화면 선택비동기 처리의 순서
getConfig()/config의 값을 입력 요소에 반영JSON 키와 폼 필드 대응
discoverHomeAssistant()/discover 결과로 연결 카드를 생성네트워크 발견 결과의 화면 표시
getWakewords()/config/wakewords 결과로 선택지 구성서버 파일 목록과 UI의 연결

관리 화면은 외부 CDN에서 Materialize의 CSS와 JavaScript를 불러오므로, 파일을 기기에 둔다고 완전한 오프라인 화면이 되는 것은 아닙니다.

3-3. 오디오와 시스템 라이브러리

구성 요소레시피 버전소스 위치
MPD0.23.16packages/mpd/package.mk
FFmpeg4.4packages/ffmpeg/package.mk
Shairport Sync4.3.5packages/shairport-sync/package.mk
Snapcast0.25.0packages/snapcast/package.mk의 Boost는 1.80.0
Upmpdcli1.8.6packages/upmpdcli/package.mk
ALSA 라이브러리1.2.7packages/alsa-lib/package.mk
Porcupine1.9packages/porcupine/package.mk
Squeezelitemaster고정 릴리스 대신 움직이는 브랜치의 소스를 다운로드

이 버전은 레시피가 요청하는 버전입니다. 레시피 선언만으로 실제 배포 이미지의 설치 버전까지 확정할 수는 없습니다. 같은 저장소 커밋이어도 범위 의존성이나 master가 가리키는 내용은 다운로드 시점에 달라질 수 있습니다.

3-4. 인프라·빌드: 오래된 대상과 현재 빌드 환경의 연결

층확인한 설정의미
컨테이너packages/Dockerfile의 ubuntu:20.04도구가 호스트 시스템을 수정하는 영향을 컨테이너 안에 가두는 용도
기본 툴체인Linaro 7.4.1-2019.02arm-linux-gnueabihf 대상; packages/toolchain/package.mk
분기 툴체인호스트가 aarch64이면 10.2-2020.11코드 분기는 있지만 README의 공식 지원 호스트와는 구분
C 런타임glibc 2.27, S12는 2.19대상 시스템과 실행 파일의 호환성 기반
빌드 도구Make·Autotools·CMake·Meson·Ninja·pkg-config각 상류 프로젝트의 서로 다른 빌드 방식을 레시피가 감쌈
CIubuntu-latest, 패키지 빌드용 이미지 :latest호스트 이미지·컨테이너 태그를 해시로 고정하지 않음

.github/workflows/build-packages.yaml은 actions/checkout@v6·actions/upload-artifact@v7를 선언합니다. .github/workflows/build-release.yaml은 actions/download-artifact@v8, docker/login-action@v4, actions/attest@v4 등을 사용합니다. 이 버전 표기는 워크플로의 선언값이며, 실행 성공 여부와는 별개입니다.

Dockerfile의 pip3 install meson ninja도 정확한 버전을 지정하지 않습니다. 버전 문자열이 여러 곳에 적혀 있어도, 입력 전체가 고정되어야 같은 결과를 다시 만들 수 있습니다. 이 차이가 재현 가능한 빌드를 공부하는 첫 단서입니다.

4아키텍처 심화 분석

두 개의 조립 경로와 네 가지 설계 패턴

4-1. 먼저 전체 시스템을 두 장으로 나눈다

한 장은 개발 컴퓨터에서 이미지를 만드는 경로이고, 다른 한 장은 완성된 이미지가 스피커에서 실행되는 경로입니다. 이 둘을 섞으면 Docker가 스피커 안에서 돌아간다거나, Python API가 펌웨어를 컴파일한다는 식의 오해가 생깁니다.

[빌드 호스트: Linux amd64] packages/*/package.mk 원본 rootfs 이미지 | | v v packages.sh Makefile: extract 의존성 순회 | 다운로드 / 패치 / 컴파일 v | squashfs-root/ v | build-packages/staging/armv7 | 개발 헤더와 라이브러리 포함 | | | v | build-packages/s2t/armv7 ------------------+ 실행에 필요한 파일만 남김 | Makefile: patch scripts/??_*.sh | Makefile: build 새 rootfs 이미지 | Makefile: release 배포용 tar 묶음

Makefile의 all: extract patch build release에는 패키지 컴파일이 포함되지 않습니다. 패키지 준비는 별도 단계이며, scripts/92_copy_build_packages.sh는 결과 폴더가 없으면 건너뜁니다. 따라서 all이라는 이름만으로 모든 입력이 자동 생성된다고 판단하면 안 됩니다. 병렬 Make 실행의 안전한 순서까지 이 선언 하나가 보장하는 것도 아닙니다.

[스피커: 기존 커널 + 수정된 rootfs] 휴대폰 / 브라우저 ------ HTTP ------> Flask API :80 | 설정 파일 / init.d / ALSA | 리눅스 서비스·오디오 장치 마이크 --> Porcupine --> 녹음 --> FFmpeg --> Home Assistant STT | 인식된 문자 | API_AVAILABLE=1일 때 /intent 조회 | | 일치함 일치 안 함 | | intent_run() HA conversation | | 기기 제어 응답 문자 | TTS --> 스피커 출력

음성 루프는 packages/porcupine/config/launcher에 있고, Flask의 /intent는 단순한 문장 매칭을 맡습니다. 서버가 음성 전체를 중계하는 구조가 아니라, 셸 런처가 Home Assistant와 직접 통신한다는 점이 중요합니다.

4-2. 패턴 ① 단계별 파이프라인과 훅

packages.sh의 commence_build()는 모든 패키지에 공통인 작업 순서를 정합니다. func_exists()로 함수가 있는지 확인하고, 레시피가 제공한 단계만 실행합니다. 공통 뼈대는 엔진이 소유하고 세부 동작은 패키지가 채우는 방식입니다.

# packages.sh의 build_stages에 선언된 순서
preconfigure_package
configure_package
premake_package
make_package
preinstall_package
install_package
postinstall_package

# 각 단계 전에 존재 여부를 확인한 뒤 호출
if func_exists ${stage}; then
    ${stage}
fi

예를 들어 packages/mpd/package.mk의 configure_package()는 Meson의 크로스 파일을 넘기고, packages/alsa-lib/package.mk는 Autotools의 ./configure에 컴파일러와 호스트 정보를 넘깁니다. 두 도구의 차이는 레시피 안에 머물고, 바깥 실행 순서는 같습니다.

reset_package_vars()는 변수뿐 아니라 이전 레시피의 훅 함수도 unset -f로 지웁니다. 이 작업이 없다면 앞 패키지의 install_package()가 뒤 패키지에 남아 실행될 수 있습니다. 전역 상태를 사용하는 설계에서는 초기화가 실행 흐름의 일부입니다.

비유

세탁소의 접수→세탁→건조→포장 순서는 같지만, 옷감마다 세제와 온도가 다릅니다. 엔진은 순서를 정하고 레시피는 처리법을 정합니다. 다음 옷을 받을 때 이전 옷감 설정을 지우는 일도 필요합니다.

4-3. 패턴 ② 의존성 그래프와 상태 캐시

process_package()는 먼저 패키지를 읽고, PACKAGE_DEPENDS에 적힌 이름들을 재귀 호출합니다. 의존성 빌드가 전역 변수를 덮어쓰므로, 돌아온 다음 현재 패키지에 대해 init_package()를 다시 호출합니다.

# packages.sh의 process_package()에서 핵심 부분 발췌
local current_package=${PACKAGE}
init_package

if [[ ! -z "${PACKAGE_DEPENDS}" ]]; then
    for depend in ${PACKAGE_DEPENDS}
    do
        process_package $depend
    done
    PACKAGE=${current_package}
    init_package
fi

PACKAGES_PROCESSED 배열은 같은 실행에서 이미 방문한 이름을 다시 순회하지 않도록 합니다. 패키지 초기화 전에 이름을 등록하므로 빌드 완료 목록은 아닙니다. 이는 중복 방지 장치이며, 순환 의존성의 경로와 원인을 알려 주는 검사기는 아닙니다. 그래프의 방문 여부와 “현재 방문 중” 상태를 분리하는 설계와 비교해 볼 수 있습니다.

update_package_status()는 build_id와 build_status를 .status 파일에 기록합니다. package_needs_building()은 완료 상태가 아니거나, 의존성이 이번 BUILD_ID에서 새로 빌드되었으면 재빌드를 요구합니다. 반환값 0은 빌드 필요, 1은 생략 가능, 2는 오류입니다.

이 캐시는 레시피 내용·컴파일 옵션·원격 소스의 해시 전체를 비교하는 방식이 아닙니다. 레시피를 바꿨는데 상태만 보고 생략할 가능성을 검토해야 합니다. “이전 실행을 기억한다”와 “현재 입력이 동일함을 증명한다”는 다른 수준의 기능입니다.

4-4. 패턴 ③ 관리 API를 시스템 명령의 어댑터로 쓰기

api/main.py의 set_volume_level()는 숫자 범위와 믹서 이름을 검사합니다. 이어서 api/utils.py의 set_volume()가 0~100 값을 0~255로 바꾸고 amixer를 호출합니다. HTTP 요청을 장치 제어로 번역하는 작은 어댑터입니다.

# api/utils.py의 set_volume()에서 발췌
volume = max(0, min(100, volume))
volume = int(volume * 255 / 100)
result = subprocess.run(
    ['amixer', 'set', mixer, f'{volume}'],
    capture_output=True,
    check=True
)

manage_service(service, action)도 같은 원리입니다. api/const.py의 services_allowed에 있는 서비스와 start·stop·restart 동작만 허용한 뒤 /etc/init.d를 호출합니다. 명령 경계에서 입력을 좁히는 방식을 배울 수 있습니다.

서비스 자체는 packages/core_api/config/api.init의 start_service()가 procd에 등록합니다. Python 프로세스와 _xiaoai._tcp mDNS 광고를 별도 인스턴스로 띄웁니다. 관리 API, 프로세스 감독, 네트워크 발견은 서로 다른 책임입니다.

소스에서 보이는 경계
Home Assistant 인증과 이 API의 접근 제어는 별개입니다

/auth와 토큰 갱신은 스피커가 Home Assistant에 접근하기 위한 흐름입니다. API 라우트에는 요청자 인증을 강제하는 공통 장치가 없고, 서버는 모든 인터페이스의 80번 포트에 바인딩합니다. 서비스 허용 목록이 있다는 사실만으로 외부에 공개해도 되는 API라고 판단할 수 없습니다.

4-5. 패턴 ④ 로컬 명령 우선, 외부 처리로 이어지는 음성 흐름

get_stt_settings()는 Home Assistant가 제공하는 샘플레이트·채널·비트 수를 읽고 런처 값을 조정합니다. 녹음 후 통째로 보내기보다 녹음→변환→전송을 파이프로 잇고, tee로 중간 음성도 남깁니다. 재생 볼륨을 줄이고 끝에 복원하는 동작까지 하나의 루프에 들어 있습니다.

api/intents.py의 get_intents()는 소문자 변환, 일부 악센트와 구두점 제거, YAML의 대체어 적용을 거친 뒤 문장 목록에서 정확히 일치하는 항목을 찾습니다. 자연어 모델이 아니라 규칙 기반 분기입니다. 성공한 로컬 명령은 런처의 intent_run()으로 실행합니다.

입력과 결과이어지는 경로읽을 지점
빈 문장/intent에서 400get_intents()의 입력 검사
일치하는 문장명령 이름 반환, 로컬 실행 분기api/intents.yaml → intent_run()
일치하지 않는 문장404, 런처가 Home Assistant 대화 API 호출INTENT_SUCCESS 조건
STT 처리 실패오류 기록·음성 안내 경로STT_SUCCESS 조건

하나의 요청이 어느 경계에서 다른 프로그램으로 넘어가는지 추적하면, 복잡한 시스템도 입력·변환·출력의 연결로 읽을 수 있습니다. 다음 절에서는 그 경계를 디렉토리 지도에 표시합니다.

5디렉토리 구조 해부

폴더 이름보다 “언제, 어디에 쓰이는가”를 기준으로 읽기

다음은 주요 경로를 골라 줄인 트리입니다. 파일 경로와 실습 명령은 최상위 Makefile이 있는 저장소 루트를 기준으로 합니다.

./ ├── Makefile 이미지 추출·수정·포장 ├── packages.sh 외부 프로그램 빌드 엔진 ├── packages/ 패키지 레시피 182개 │ ├── Dockerfile 호스트 빌드 환경 │ ├── toolchain/ 컴파일러·크로스 설정 │ ├── glibc/ 기기용 C 런타임 │ ├── mpd/ 재생 엔진과 설정 │ ├── ffmpeg/ 오디오 변환 도구 │ ├── porcupine/config/ 깨우기·음성 루프·서비스 │ └── core_api/ API 설치·부팅 등록 ├── scripts/ rootfs 수정 단계 │ ├── 10_apply_patches.sh 공통·모델별 패치 적용 │ ├── 92_copy_build_packages.sh 빌드 결과 병합 │ └── 98_buildinfo.sh 버전·빌드 정보 갱신 ├── patches/ 원본 시스템에 적용할 차이 │ ├── lx01/ 모델별 설정 │ ├── lx05/ │ ├── lx06/ │ └── l09a/ ├── api/ Python 관리 API │ ├── main.py 라우트와 인증 연동 │ ├── config.py 파일 설정 읽기·쓰기 │ ├── utils.py 시스템 정보·볼륨 제어 │ ├── intents.py 규칙 기반 의도 매칭 │ └── static/ HTML·JavaScript 화면 ├── bin/ 기기에 넣을 셸 도구 ├── tools/ 펌웨어 형식·추출 도구 ├── research/ 모델 조사·사진·부팅 로그 ├── custom_components/xiaoai_assist/ Home Assistant 연동 초안 ├── docs/ FAQ·음성비서 구성 문서 ├── sound/ 안내음 파일 └── .github/workflows/ 패키지·릴리스 자동화

5-1. 이름이 비슷한 두 종류의 패치를 구분한다

경로대상적용 주체
patches/*.patch스피커 원본 파일시스템의 파일scripts/10_apply_patches.sh
patches/lx06/*.patch 등특정 모델의 설정과 시작 스크립트공통 패치 뒤 모델별 패치 순서
packages/*/patches/다운로드한 외부 프로그램의 소스packages.sh의 apply_patches()

세 번째 경로는 상류 프로그램이 오래된 기기 환경에서 컴파일되도록 조정하는 데 쓰입니다. 첫 두 경로는 기기에 들어갈 파일의 동작을 바꿉니다. 같은 patch 명령을 써도 입력과 결과가 다릅니다.

5-2. 파일·줄 수로 보는 규모

집계 기준은 git ls-files의 추적 파일입니다. 줄 수는 NUL 바이트가 없고 UTF-8로 읽히는 텍스트의 줄바꿈 수이며, 빈 줄과 주석을 포함합니다. 일반 파일을 가리키는 심볼릭 링크는 대상 내용을 각각 세고, 끊어진 링크와 디렉토리 링크는 제외합니다. 바이너리·이미지·압축 소스도 제외합니다. 외부 프로젝트 전체의 소스 규모나 실행 코드만의 줄 수를 뜻하지 않습니다.

분류파일 수줄 수
최상위 Makefile1196
package.mk 셸 레시피1824,905
셸 스크립트473,782
Python121,618
JavaScript / HTML각 187 / 50
C 헤더1713
차이 파일935,669
YAML / YML합계 9419
Markdown111,395
기타 텍스트385,281
텍스트 합계39624,115

추적 파일 전체는 418개입니다. 셸 분류에는 확장자가 .sh인 파일과, 확장자에 관계없이 #!/bin/sh·#!/bin/bash로 시작하는 파일을 포함합니다. API 폴더의 Python 5개는 752줄이며, 전체 Python 1,618줄과 구분됩니다. C/C++ 구현이 이 저장소 안에 대량 포함되어 있다는 뜻도 아닙니다.

5-3. 초보자를 위한 세 가지 읽기 경로

  1. 이미지 경로: Makefile → scripts/10_apply_patches.sh → 모델 패치 → scripts/92_copy_build_packages.sh.
  2. API 경로: api/static/app.js → api/main.py → api/config.py 또는 api/utils.py.
  3. 음성 경로: docs/voice-assistant.md → packages/porcupine/config/launcher → api/intents.py → api/intents.yaml.

처음부터 182개 레시피를 순서대로 읽을 필요는 없습니다. 작은 요청 하나를 고르고, 그것이 지나가는 파일을 끝까지 따라가는 편이 구조를 이해하기 쉽습니다.

6학습 포인트 (기술별)

좋은 구조와 개선할 지점을 함께 읽어야 실력이 된다

6-1. 셸: 표준 입출력으로 프로그램 연결하기

런처의 음성 전송은 유닉스 파이프의 실제 사례입니다. 녹음 도구는 소리를 바이트로 내보내고, FFmpeg는 형식을 바꾸며, curl은 그 바이트를 전송합니다. 각 프로그램이 상대방의 내부 구현을 몰라도 표준 입출력이라는 약속으로 협력합니다.

# launcher의 긴 전송 구간을 구조만 남겨 표현한 도식
녹음 명령
  | tee /tmp/input.wav
  | FFmpeg 형식·채널·샘플레이트 변환
  | tee /tmp/stt.wav
  | curl로 Home Assistant에 전송

tee는 관찰을 쉽게 하지만 음성과 로그가 임시 파일에 남습니다. cleanup()은 일부 파일을 지우고 log()는 /tmp/stt.log에 문자를 기록합니다. 디버깅 가능성과 데이터 보관 범위가 같은 코드에서 결정되는 것입니다.

실습 아이디어: 각 파이프의 입력 형식·출력 형식·실패 시 표시를 종이에 적어 보세요. 런처는 ffmpeg 존재 여부를 확인하는 구간이 있지만, 전송 파이프에서는 여전히 ffmpeg를 호출합니다. 이 정적 관찰을 “대체 경로가 완성되어 있는가?”라는 검토 질문으로 바꾸면 좋습니다.

6-2. 종료 코드: 실패가 어디서 사라지는가

scripts/10_apply_patches.sh는 여러 패치를 적용한 뒤 마지막에 true를 실행합니다. 이 스크립트에는 즉시 중단 설정이 없어서, 중간의 패치 실패가 최종 종료 코드에 반영되지 않을 수 있습니다. 바깥 Makefile이 스크립트 종료 코드를 검사하더라도, 안쪽에서 성공으로 바뀌면 놓칠 수 있습니다.

# scripts/10_apply_patches.sh의 핵심 발췌
for PATCHFILE in patches/*.patch patches/${MODEL}/*.patch; do
  patch -p1 --no-backup-if-mismatch -r /dev/null \
    -d ${ROOTFS} < ${PATCHFILE}
done

true

실습 아이디어: 첫 번째·중간·마지막 패치가 실패하는 경우를 각각 추적해 보세요. “실패 로그가 있다”와 “상위 작업이 실패한다”를 다른 칸에 적습니다. 일반적인 파이프 종료 상태는 GNU Bash 공식 문서와 대조할 수 있습니다.

여기서 곧바로 모든 곳에 set -e를 넣자는 결론을 내릴 필요는 없습니다. 의도적으로 실패를 허용한 단계도 있을 수 있으므로, 어떤 오류는 계속 진행하고 어떤 오류는 중단할지 계약부터 정하는 것이 학습의 핵심입니다.

6-3. 크로스컴파일: CPU 이름만 맞으면 충분한가

packages/toolchain/package.mk의 on_exit_build()는 컴파일러뿐 아니라 BUILD_CFLAGS·BUILD_LDFLAGS·pkg-config 경로를 함께 설정합니다. 헤더·링커·동적 로더·공유 라이브러리가 대상 시스템과 맞아야 실행할 수 있기 때문입니다.

research/lx06/boot에는 AArch64 커널과 32비트 사용자 공간 지원이 보이지만, 기본 패키지 출력은 armv7입니다. CPU가 64비트를 지원한다는 사실만으로 사용자 공간 전체를 64비트로 바꿀 수 있다고 판단하면 안 됩니다.

실습 아이디어: MPD 레시피에서 컴파일러·sysroot·pkg-config·설치 경로가 어디서 오는지 선으로 연결하세요. packages/toolchain/config/meson-cross-file.in과 CMake 템플릿을 비교하면 같은 대상 정보를 도구별로 표현하는 방법을 배울 수 있습니다.

6-4. 오디오: 형식·샘플레이트·채널을 분리하기

런처 기본 녹음 설정은 16,000Hz·1채널이며, STT 전송 기본값은 8,000Hz·16비트·1채널입니다. 이후 get_stt_settings()가 제공자 응답에 맞춰 전송 값을 조정합니다. 문서의 설정 예시는 STT_RATE=16000이므로 코드의 기본값과 같지 않습니다.

# 압축하지 않은 PCM 데이터량의 개념 계산
바이트/초 = 샘플레이트 × 채널 수 × 비트 수 ÷ 8
16,000 × 1 × 16 ÷ 8 = 32,000바이트/초
5초 녹음 ≈ 160,000바이트

# WAV 헤더·네트워크 부가 정보는 제외한 값

실습 아이디어: 48,000Hz·2채널·16비트로 바꾸면 초당 192,000바이트가 됩니다. 같은 길이라도 왜 메모리·전송 비용이 6배가 되는지 계산해 보세요. 이어서 packages/mpd/config/mpd.conf의 FIFO 형식 48000:16:2를 해석해 봅니다.

6-5. HTTP 계약: 선언한 기능과 실행 분기를 맞춰 보기

api/intents.yaml에는 Next 명령이 있고, get_intents()는 일치하면 그 이름을 반환합니다. 그러나 런처의 intent_run()에는 Next 처리 분기가 없습니다. 로컬 매칭은 성공해서 Home Assistant 경로도 생략할 수 있으므로, 목록에 있다는 사실만으로 실제 동작까지 구현되었다고 볼 수 없습니다.

실습 아이디어: YAML의 모든 action과 런처 분기 목록을 대조하는 표를 만들어 보세요. 반환 가능한 값과 처리 가능한 값의 집합이 같은지 확인하는 일은 API·이벤트·메시지 시스템에서도 반복되는 작업입니다.

ConfigManager.to_dict()는 이름에 token이 들어간 키를 API 응답에서 제외합니다. 이는 표시 범위를 줄이는 처리이며, 파일 자체의 암호화나 접근 제어를 구현한 것은 아닙니다. 데이터를 읽는 곳·저장하는 곳·보여 주는 곳을 나눠 생각해 보세요.

6-6. 파일 형식: 무결성 검사와 진위 검증의 차이

tools/mico_firmware.py의 ImageHeader·SegmentHeader는 바이너리 구조를 정의합니다. Firmware.verify()는 매직 값 0x31524448·0x32524448, 서명 영역의 길이, CRC32, 파일명에 포함된 MD5 일부를 검사합니다.

성공 로그는 제조사 정품 펌웨어라는 취지의 표현을 쓰지만, 이 함수에서 공개키로 서명을 암호학적으로 검증하는 연산은 보이지 않습니다. signature라는 변수명이나 해시 일치만으로 누가 만든 파일인지 증명했다고 해석하면 안 됩니다.

실습 아이디어: “형식이 맞는가”, “데이터가 손상되지 않았는가”, “신뢰하는 제작자가 만들었는가”를 세 열로 나누고 각 검사를 분류하세요. 이 파일은 실행하지 말고 구조체 필드와 verify()의 읽기 순서를 추적합니다.

소스 읽기는 함수 이름을 외우는 일이 아니라, 입력을 받아 약속한 결과를 끝까지 내보내는지 확인하는 일입니다. 잘 만든 연결과 빠진 연결을 함께 찾는 습관이 중요합니다.

7하드웨어 / 시스템 요구사항

문서 지원·빌드 분기·실제 기기 검증은 서로 다른 정보

7-1. 공부용 컴퓨터와 빌드용 컴퓨터

목적필요한 환경확인 범위
이 글의 읽기 실습복제된 소스, 텍스트 편집기, 셸, 선택적으로 Python 3스피커·Docker·GPU 불필요
패키지·이미지 빌드README 기준 Linux amd64, Docker, make, rsync, shasum, squashfs-tools호스트 지원 범위는 README 기준
S12 이미지 처리README의 mtd-utils와 UBI 관련 도구기본 SquashFS 경로와 다른 특수 분기
음성비서 실제 구성호환 스피커, Home Assistant, 사용 가능한 STT 제공자, 네트워크제공자와 TTS 선택에 따라 외부 통신 범위가 달라짐

README는 패키지 빌드에 1시간 이상이 걸릴 수 있다고 안내합니다. 호스트 RAM·디스크의 확정 최소치는 README에 명시되어 있지 않습니다.

코드에 aarch64 호스트 분기가 있어도 README는 Apple M1·Windows 호스트를 미지원·미검증으로 표시합니다. 반면 LX06의 설치 안내는 Ubuntu 24.04와 Windows 10의 USB 기록 절차를 설명합니다. 빌드 호스트 지원과 기록 도구 지원을 분리해야 두 설명이 모순처럼 보이지 않습니다.

7-2. 모델별 상태를 읽는 방법

모델README 기준코드·문서에서 덧붙일 점
LX06 / L06A지원, 대상 버전 1.74.10LX06 문서는 이 계열을 가장 많이 시험한 모델로 소개·권장; Pro에 적외선 송신 기능
LX01지원, 대상 버전 1.32.6Upmpdcli 제외, Snapcast 클라이언트만 빌드하는 모델 분기
L09A지원, 대상 버전 1.76.4전용 ALSA·부팅 패치 존재
L09B대상 버전 1.69.54, 미검증 문구릴리스 CI 행렬에 있어도 실물 검증의 증거는 아님
LX05 / L15A서명·암호화 관련 제약 표시미서명 이미지가 차단될 수 있다는 README 경고를 함께 읽어야 함
L09G / L05B·L05C미지원비슷한 제품명이나 코드 조각만으로 지원을 추론하지 않기

Home Assistant 사용자 구성 요소의 DEVICES_COMPATIBLE는 LX06·L06A·L09A만 포함합니다. 펌웨어 패치 지원 범위와 사용자 구성 요소가 받아들이는 모델 목록도 같지 않습니다.

7-3. 저장 공간은 이미지 형식과 함께 봐야 한다

모델Makefile 설정의미
기본BLOCKSIZE=131072, COMPRESSION=xz128KiB 블록을 사용하는 기본 SquashFS 생성 옵션
LX01블록 262144, 크기 기준 30408704바이트256KiB 블록, 29MiB 크기 비교 기준
LX06 / L09A크기 기준 41943040바이트40MiB 크기 비교 기준
LX05블록 262144, COMPRESSION=gzip기본 모델과 압축 옵션이 다름

build_squashfs는 크기가 지정된 기준보다 크거나 같으면 경고를 출력하고 종료 코드 1로 중단합니다. 표의 숫자만으로 실제 파티션의 여유 공간까지 보장되지는 않습니다. 리눅스 커널 문서가 설명하듯 SquashFS는 압축된 읽기 전용 파일시스템이므로, 앱을 넣을 때 이미지 자체를 다시 만드는 방식이 자연스럽습니다.

LX06 문서는 RAM 128MB·256MB 변형을 적고 있습니다. MPD 설정의 input_cache는 20MB이며, 업데이트 도구는 기본 가용 메모리 기준을 45000KB로 둡니다. 모두 같은 “메모리” 이야기지만, 물리 RAM·프로세스 버퍼·업데이트 시 남아 있어야 할 공간은 별도 항목입니다.

기기별로 확인할 사실
최신 LX06 문서가 알려 주는 커널과 NAND의 결합

커밋 c26bef8의 LX06 설치 문서는 일부 Foresee/ESMT FSNS8A001G NAND를 2021-06-28 빌드의 배포 커널이 인식하지 못해 부팅이 반복될 수 있다고 설명합니다. 기존 커널이 정상이라면 보존할 수 있다는 안내이며, rootfs 수정과 커널 교체를 같은 작업으로 취급하지 말라는 사례입니다.

복구에는 해당 기기의 원본 백업과 부트로더 접근 여부가 중요합니다. 이 글은 실물 기록 절차를 검증한 설치 매뉴얼이 아니며, 아래 실습도 파티션 기록을 포함하지 않습니다.

지원 모델 이름이 같아도 메모리·무선 칩·NAND·기존 커널이 다를 수 있으므로, 호환성은 부품과 버전의 조합으로 판단해야 합니다.

8직접 해볼 수 있는 실습 과제

소스를 읽고 설명하는 5단계 과제 — 기기·설치·빌드 없이 수행

모든 과제의 명령은 저장소 루트에서 읽기 전용으로 사용합니다. Makefile과 packages.sh 등의 내용을 출력하거나 검색하는 명령이며, 저장소의 Python 모듈을 import하거나 셸 스크립트를 실행할 필요는 없습니다.

과제 1 · 입문

이미지 제작 단계를 말로 복원하기 예상 20~30분

할 일: Makefile에서 extract·patch·build·release를 찾아 각 입력과 출력을 한 문장씩 적습니다. 기본 실행 목표가 help라는 사실도 확인합니다.

완료 기준: “패키지를 빌드하는 단계”와 “이미지를 포장하는 단계”를 구분하고, all에 패키지 빌드가 포함되지 않는 이유를 설명할 수 있습니다.

# 저장소 루트에서 파일을 읽기만 합니다
sed -n '1,110p' Makefile
rg -n '^all:|^extract|^patch:|^build|^release' Makefile
sed -n '1,35p' scripts/92_copy_build_packages.sh
과제 2 · 입문

볼륨 50 요청의 여행 그리기 예상 30분

할 일: set_volume_level() → set_volume() → amixer 순서로 흐름을 그립니다. 50이 내부 값 127로 바뀌는 계산과, 잘못된 숫자·범위·믹서 이름에 대한 응답을 기록합니다.

완료 기준: 입력 검사와 장치 제어를 나누어 설명하고, -1·101·숫자가 아닌 값·허용하지 않은 믹서의 결과를 코드로 설명합니다. 실제 HTTP 요청은 보내지 않습니다.

sed -n '173,207p' api/main.py
sed -n '50,87p' api/utils.py
rg -n 'volume_controls|mysoftvol|notifyvol' api/const.py
과제 3 · 중급

기능 목록과 실행 분기의 차이 찾기 예상 40분

할 일: YAML의 모든 action을 목록으로 적고 intent_run()의 조건과 대조합니다. Next가 반환되었을 때 로컬 실행과 Home Assistant 대체 경로가 각각 어떻게 되는지 추적합니다.

완료 기준: 선언됨·매칭됨·실행됨을 세 열로 나눈 표를 완성합니다. 개선안은 “명령 추가”뿐 아니라 “실행 불가를 돌려주고 대체 경로로 넘김”도 비교합니다. 원본 파일은 고치지 않습니다.

rg -n 'action:|Next' api/intents.yaml
sed -n '1,110p' api/intents.py
sed -n '146,167p' packages/porcupine/config/launcher
sed -n '264,309p' packages/porcupine/config/launcher
과제 4 · 중급

의존성 2단계 지도와 캐시 판단표 만들기 예상 60분

할 일: core_api → python3 → glibc·ncurses·expat 지도를 그립니다. 이어서 “이전 완료 상태”, “의존성의 이번 실행 재빌드 여부”에 따라 현재 패키지가 빌드되는지 표로 정리합니다.

완료 기준: package_needs_building()의 반환값 0·1·2를 해석하고, 레시피 내용이 바뀐 경우를 현재 캐시가 직접 감지하는지 근거를 제시합니다.

sed -n '1,15p' packages/core_api/package.mk
sed -n '1,10p' packages/python3/package.mk
sed -n '425,456p' packages.sh
sed -n '549,599p' packages.sh
과제 5 · 고급

재현성과 실패 전달을 개선하는 설계 메모 예상 90분 이상

할 일: 범위 버전, master 다운로드, 컨테이너 :latest, 패치 스크립트의 마지막 true를 찾아 개선안을 씁니다. 입력 해시·의존성 결과 해시·도구 버전을 캐시 키에 어떻게 넣을지 설계합니다.

완료 기준: “항상 빌드하면 된다”에서 끝내지 않고, 재빌드 조건과 중단 조건을 각각 정의합니다. 새 설계에서 BUILD_ID와 입력 해시를 어떻게 활용할지 설명하고 검증 시나리오 3개를 작성합니다.

cat api/requirements.txt
sed -n '1,12p' packages/squeezelite/package.mk
cat scripts/10_apply_patches.sh
cat .github/workflows/build-packages.yaml

실습의 성공 기준은 스피커를 바꾸는 데 있지 않고, 코드가 약속한 동작을 근거 파일과 함께 설명할 수 있게 되는 데 있습니다.

9관련 기술 심화 학습 로드맵

한 주에 하나의 경계를 이해하고 작은 설명 자료로 남기기

아래 로드맵은 레포의 도구를 실행하는 일정이 아니라, 읽은 코드를 일반적인 시스템 지식으로 확장하는 일정입니다. 하드웨어 작업은 별도 실습으로 분리하고, 먼저 모델과 무관하게 설명할 수 있는 개념을 익힙니다.

주차학습 주제·레포 연결남길 결과물
1주차리눅스 경로·권한·읽기 전용 파일시스템. Makefile과 scripts/94_copy_bin.sh 읽기원본 이미지→디렉토리→수정 이미지의 1장 흐름도
2주차셸 변수·함수·종료 코드·파이프. commence_build()와 런처 비교성공·실패가 상위 단계로 전달되는 규칙표
3주차컴파일·링크·ABI·sysroot. 툴체인과 MPD 레시피 읽기호스트 파일과 대상 파일을 나눈 경로 지도
4주차HTTP·JSON·상태 코드·설정 저장. /volume·/config 추적요청별 입력·검사·부작용·응답 계약 문서
5주차PCM·샘플레이트·채널·STT·TTS. 런처와 packages/mpd/config/mpd.conf 읽기오디오 변환 지도와 전송량 계산표
6주차CI·아티팩트·재현성·업데이트. 워크플로와 bin/update.sh 읽기버전 고정·무결성·복구 경계를 설명한 설계 검토

9-1. 셸 다음에는 프로세스의 생명주기로 확장하기

packages/porcupine/config/porcupine.init의 start_service()·stop_service()·reload_service()를 비교하세요. 시작 명령만이 아니라 자식 프로세스 정리, PID 파일, LED 상태, 재시작 정책이 하나의 서비스 경험을 만듭니다.

API와 리스너 모두 procd_set_param respawn 3600 5 0을 선언합니다. 숫자의 정확한 의미는 사용하는 procd 버전 문서와 대조해 읽고, “재시작 설정이 있다”는 사실만으로 장애 복구가 모두 해결된다고 해석하지 않습니다.

9-2. 업데이트 다음에는 상태 전이로 확장하기

bin/update.sh의 get_boot() → set_target_partitions() → flash_image() → switch_boot()를 종이에 이어 보세요. 현재 사용하지 않는 파티션을 고르는 의도와, 기록 후 다음 부팅 대상을 바꾸는 순서를 이해하는 것이 목표입니다.

이어 “기록 명령이 실패하면 부팅 전환을 막는가?”를 점검합니다. flash_image()의 실패 경로에는 오류 출력으로 이어지는 분기가 있고, 호출부에는 파이프도 있습니다. 실행하지 않고도 실패를 상위로 확실히 전달하는 설계인지 검토할 수 있습니다.

9-3. 마지막에는 제품의 약속을 검토하기

“지원한다”, “로컬이다”, “오픈소스다”, “검증했다” 같은 표현을 보이면 무엇을 근거로 하는지 되묻습니다. 예를 들어 Porcupine 레시피는 C 예제를 컴파일하면서 상류의 libpv_porcupine.so도 복사합니다. 저장소의 빌드 코드가 공개되어 있다는 것과 모든 포함 바이너리의 내부 구현도 저장소에 포함된다는 것은 다릅니다.

Home Assistant 구성 요소의 custom_components/xiaoai_assist/manifest.json은 0.0.1이고, 해당 디렉토리에는 구성 흐름과 상수·번역 등이 있습니다. 완성된 엔티티 플랫폼으로 취급하기보다 validate_input()의 연결 검사와 설정 흐름을 배우는 자료로 읽는 편이 정확합니다.

주간 회고 기준
설명은 입력·상태·출력 세 문장으로

“무엇을 받는가?”, “무엇을 바꾸는가?”, “실패를 어떻게 알리는가?”에 답해 보세요. 셋 중 하나가 막히면 해당 함수의 호출자와 반환값을 다시 읽으면 됩니다.

학습의 끝은 특정 스피커의 명령을 외우는 것이 아니라, 다른 리눅스 장치에서도 같은 경계를 찾아 설명할 수 있게 되는 것입니다.

10핵심 키워드 사전

이 글에서 만난 단어를 실제 코드의 자리와 연결하기
용어쉬운 설명
펌웨어장치를 구동하는 소프트웨어 묶음. 이 프로젝트는 기존 묶음의 사용자 공간을 중심으로 바꿉니다.
커널CPU·메모리·장치를 관리하는 리눅스 핵심. rootfs를 바꾸는 일과 커널을 바꾸는 일은 다릅니다.
rootfs장치의 / 아래에 놓이는 실행 파일·설정·라이브러리의 기본 묶음입니다.
SquashFS파일들을 압축해 저장하는 읽기 전용 파일시스템. 이미지에 앱을 추가하려면 다시 포장합니다.
UBI / UBIFS플래시 메모리를 다루는 계층과 그 위의 파일시스템. 이 레포의 S12 처리 경로에 등장합니다.
파티션저장 장치를 역할별로 나눈 구역. 커널·시스템·사용자 데이터를 서로 다른 구역에 둘 수 있습니다.
부트로더전원을 켰을 때 운영체제를 시작하는 프로그램. 여기서는 U-Boot가 복구·부팅 선택과 연결됩니다.
A/B 구성두 부팅 영역을 번갈아 사용하는 구성. 영역 두 개만으로 자동 복구 전체가 보장되지는 않습니다.
크로스컴파일개발 컴퓨터에서 다른 종류의 기기용 실행 파일을 만드는 작업입니다.
툴체인컴파일러·링커·바이너리 도구 등 빌드에 필요한 도구 묶음입니다.
ABI실행 파일과 라이브러리가 바이트 수준에서 맞춰야 하는 약속. CPU 명령뿐 아니라 호출 규칙 등이 관련됩니다.
sysroot대상 기기의 헤더·라이브러리 구조를 빌드 호스트에서 참조하도록 준비한 기준 디렉토리입니다.
glibc여러 리눅스 프로그램이 사용하는 기본 C 라이브러리. 대상 버전과 실행 환경의 결합이 중요합니다.
staging다른 패키지를 빌드할 때 쓸 헤더·라이브러리까지 모아 두는 중간 설치 공간입니다.
훅공통 처리의 정해진 시점에 끼워 넣는 함수. configure_package() 등이 예입니다.
의존성 그래프무엇을 먼저 준비해야 하는지 나타내는 연결 지도. PACKAGE_DEPENDS가 간선을 표현합니다.
증분 빌드필요한 부분만 다시 만드는 방식. 이 프로젝트는 이전 완료 상태와 의존성 빌드 정보를 활용합니다.
ALSA리눅스 오디오 장치를 다루는 기반. amixer와 aplay가 제어·재생에 쓰입니다.
PCM시간에 따른 소리의 크기를 숫자로 나타낸 데이터. 샘플레이트·비트 수·채널 수를 함께 봅니다.
STT / TTS각각 음성을 문자로, 문자를 음성으로 바꾸는 처리. 제공자와 실행 위치는 별도로 선택됩니다.
깨우는 말음성 입력을 시작하게 하는 정해진 단어. 이 구성에서는 Porcupine이 감지합니다.
의도문장을 실행 가능한 명령 이름으로 바꾼 결과. 이 레포의 로컬 매칭은 YAML 목록을 이용합니다.
mDNS / Zeroconf같은 네트워크에서 서비스의 주소와 정보를 자동으로 찾게 하는 방식입니다.
procdOpenWrt 계열의 프로세스·서비스 관리 도구. 이 레포의 init 스크립트가 사용합니다.
무결성데이터가 기대한 값과 일치하는 성질. 해시 비교와 제작자 신원 검증은 구분해야 합니다.
아티팩트빌드가 만든 보관·배포 대상 파일. 여기서는 패키지 압축 파일과 펌웨어 묶음입니다.

새 단어를 만나면 이 표의 정의를 읽고, 실제 사용하는 파일 한 곳을 다시 찾아보세요. 예를 들어 ABI는 툴체인 설정, STT는 런처의 HTTP 헤더, mDNS는 packages/core_api/config/api.init에서 확인할 수 있습니다.

11참고 링크

코드의 기준점과 문서의 주장, 외부 기술 설명의 출처

11-1. 저장소와 핵심 소스

아래 코드 링크와 글 속 파일 경로는 커밋 c26bef8 기준이며, 기본 브랜치의 미래 상태와 다를 수 있습니다.

11-2. 설치 문맥과 관련 기술의 공식 자료

현재 상태 (2026-10-23 확인)

항목상태근거·범위
스타 / 포크492 / 542026-10-23 GitHub 기준
생성일2020-12-132026-10-23 GitHub 기준
최근 push2026-09-292026-10-23 GitHub 기준
최신 릴리스2026.3.26 · 2026-03-162026-10-23 GitHub 기준 태그·공개일
최근 커밋c26bef8 · 2026-09-29LX06 Foresee/ESMT NAND 부팅 반복 우회 문서, #131
주 언어Makefile2026-10-23 GitHub 기준; 실제 package.mk는 Bash로 로드
코드 규모추적 418파일, 텍스트 396파일·24,115줄5-2절 집계 기준; 문서·패치·로그·설정·주석 포함
빌드 레시피182개·4,905줄packages/*/package.mk
API·화면API Python 752줄, JavaScript 87줄, HTML 50줄API Python은 전체 Python 1,618줄의 일부
라이선스GPL-3.02026-10-23 GitHub 기준 및 LICENSE; 외부 포함물은 별도 확인 대상
검증 범위코드·설정의 정적 분석기기 부팅·녹음·이미지 빌드의 성공은 환경별 검증 필요
선정 순위TrendShift Daily #242026-10-23 TrendShift 기준

코드의 동작 설명은 커밋 c26bef8을, 지원 범위·소요 시간은 README와 모델 문서를 기준으로 합니다. 주목받는 이유에 대한 설명은 기술적 해석입니다. 관련 기술의 공식 문서는 개념을 더 살펴볼 때 참고할 수 있습니다.

쓰기 전에 알아둘 한계
공부자료를 바로 적용 가능한 설치 보증서로 읽지 마세요

이 자료는 코드·설정의 구조를 설명합니다. 실제 설치와 음성 동작의 성공 여부는 원본 이미지·모델 변형·연결한 서비스 버전에 따라 달라집니다.

README는 오픈소스 도구를 이용한 사용자 정의 시스템을 목표로 설명하지만, 기본 음성 런처는 SPEAK=tts_google을 사용하고 bin/tts_google는 Google로 요청합니다. 완전한 로컬 구성을 원한다면 STT·대화 처리·TTS 각각의 실행 위치를 따로 점검해야 합니다. bin/speak에는 로컬 eSpeak 경로가 있습니다.

SSH 패치는 root 비밀번호 로그인을 켜고, 기본 비밀번호를 root로 설정하는 스크립트가 있습니다. 관리 API의 요청자 인증, 파일 권한, 네트워크 공개 범위는 추가 검토할 실제 항목입니다. 하드웨어 지원 배지·릴리스 빌드 목록만으로 운영 안정성을 판단하지 마세요.

포함물 전체의 라이선스가 저장소 표기 하나로 정리되는 것도 아닙니다. 예를 들어 FFmpeg 레시피에는 --enable-gpl --enable-nonfree가 선언되어 있습니다. 개별 바이너리의 배포 조건은 포함된 구성 요소별로 확인해야 합니다.