Asahi Linux (ARM) ↔ Bambu P1S · LAN 직결 · 실측 기반 정리
핵심 한 줄: 프린터는 "UI 기기"가 아니라 "네트워크 API를 노출하는 IoT 장치"다.
Bambu Studio·Handy·OrcaSlicer 같은 앱은 전부 그 API 위에 얹힌 껍데기(GUI)일 뿐이다. 그래서 앱 없이 MQTT + FTPS 프로토콜만 직접 말하면 상태 읽기·인쇄·제어가 모두 된다. "완료되면 알림" 같은 자동화가 되는 이유가 바로 이것 — UI 의존이 아니라 프로토콜 기반이기 때문이다.
| 채널 | 포트 | 용도 | 인증 |
|---|---|---|---|
| MQTT (TLS) | 8883 | 상태 구독(report) + 명령 발행(request) — 실시간 텔레메트리·인쇄 제어 | bblp / 액세스코드, 자체서명 TLS |
| FTPS (implicit) | 990 | 파일 업/다운/삭제 — gcode·3mf·타임랩스·카메라 캡처·로그 | bblp / 액세스코드 |
| RTSP (카메라) | 6000 | 내장 카메라 라이브 스트림(rtsps://…/streaming/live/1) | 액세스코드 |
| SSDP (발견) | 2021 UDP | 멀티캐스트로 프린터 자동 탐지(시리얼·모델·IP) | 없음 |
request 토픽으로 명령을 쏘고 report 토픽으로 상태 JSON을 계속 돌려받는다. 이 구독→조건→행동 루프가 "완료 알림" 같은 자동화의 뼈대다.MQTT 토픽: device/<시리얼>/request(명령) · device/<시리얼>/report(상태). 시리얼은 SSDP 응답에 담겨 자동 획득된다(프린터별 고유값이라 여기선 생략).
print 객체만 63개 필드pushall 한 번으로 프린터가 뱉는 상태. 앱 화면에 안 보이는 내부값까지 전부 노출된다. 아래는 그 63개 필드를 성격별로 묶은 것.
pushall 응답 63필드의 구성(성격별 묶음, 크기 = 필드 수). 진행 상태와 AMS·필라멘트 정보가 가장 두껍고, 온도·팬·오류·카메라·하드웨어·펌웨어까지 프린터 내부가 통째로 보인다.gcode_state: IDLE/PREPARE/RUNNING/PAUSE/FINISH/FAILED)n/총, 남은 시간mc_print_stage·stg_cur), 현재 gcode 라인, 파일명·task/project IDvt_tray), 로드된 트레이(tray_now)nozzle_temper/_target)chamber_temper)cooling_fan_speed), 보조/챔버 팬(big_fan1/2)print_error 코드, lifecycleipcam)wifi_signal), 온라인 여부, SD카드 상태, 업로드 진행nozzle_diameter/_type)lights_report), 속도 레벨(spd_lvl), 캘리브레이션 버전get_version)예를 들어 AMS는 슬롯마다 필라멘트 종류·색·잔량%·건조도가 그대로 나오고, WiFi 신호 세기·노즐 지름 같은 하드웨어 값까지 실시간으로 읽힌다.
실행함 = 실제로 돌려서 확인 · 문서화 = 프로토콜상 가능(라이브 인쇄 방해하지 않으려 미실행)
| 동작 | 방법 (MQTT request 페이로드 / 프로토콜) | 상태 |
|---|---|---|
| 전체 상태 읽기 | {"pushing":{"command":"pushall"}} | 실행함 |
| 펌웨어/모듈 조회 | {"info":{"command":"get_version"}} | 실행함 |
| 인쇄 시작(파일) | {"print":{"command":"project_file","url":"file:///sdcard/…","param":"Metadata/plate_1.gcode","use_ams":true,"ams_mapping":[슬롯]}} | 실행함 |
| 일시정지 / 재개 / 정지 | {"print":{"command":"pause"}} · resume · stop | 문서화 |
| 인쇄 속도 변경 | {"print":{"command":"print_speed","param":"2"}} (1느림~4최속) | 문서화 |
| 임의 G-code 주입 | {"print":{"command":"gcode_line","param":"M104 S220\n"}} → 노즐/베드 온도(M104/M140)·팬(M106)·이동(G0/G1)·홈(G28)·압출(G1 E) 등 무엇이든 | 문서화 |
| 챔버 조명 on/off | {"system":{"command":"ledctrl","led_node":"chamber_light","led_mode":"on"}} | 문서화 |
| AMS 필라멘트 교체/설정 | {"print":{"command":"ams_change_filament",…}} / ams_filament_setting / unload_filament | 문서화 |
| 캘리브레이션(베드 레벨·공진·플로우) | {"print":{"command":"calibration",…}} | 문서화 |
| AI 모니터링(스파게티 감지) on/off | {"xcam":{"command":"xcam_control_set",…}} | 문서화 |
| 파일 업로드/목록/다운/삭제 | FTPS 990 (curl -T / ftplib) — gcode·3mf | 실행함 |
| 타임랩스·카메라 캡처·로그 다운로드 | FTPS /timelapse /image /logger | 문서화 |
| 카메라 라이브 보기/녹화 | RTSP 6000 → ffmpeg/mpv rtsps://bblp:<코드>@<프린터-ip>:6000/streaming/live/1 | 문서화 |
| 프린터 자동 발견 | SSDP 멀티캐스트 → 시리얼·모델·IP | 실행함 |
즉 앱이 하는 거의 모든 것(인쇄·모니터·온도/팬/조명 제어·AMS·캘리브·카메라)이 스크립트로 된다. 앱은 이 명령들을 버튼에 매핑한 UI일 뿐이다.
이 모든 게 되는 이유는 특정 OS라서가 아니라, 표준 네트워크 프로토콜 + 순수 파이썬이기 때문이다.
| 구성요소 | 정체 | macOS | Windows | Linux |
|---|---|---|---|---|
| MQTT(paho-mqtt) | 순수 파이썬 라이브러리 | ✅ | ✅ | ✅ |
| FTPS(ftplib/curl) | 표준 라이브러리 / curl | ✅ | ✅ | ✅ |
| RTSP 카메라(ffmpeg) | 크로스플랫폼 툴 | ✅ | ✅ | ✅ |
| SSDP 발견 | UDP 소켓 | ✅ | ✅ | ✅ |
같은 스크립트를 그대로 복사해 돌리면 어느 OS에서든 똑같이 작동한다. 같은 WiFi(또는 VPN으로 원격)에 프린터만 있으면 된다. 실제로 Bambu 공식 Handy(폰)·Studio(맥/윈)도 내부적으로는 이 MQTT/FTPS를 그대로 쓴다.
차이가 났던 딱 한 가지는 OrcaSlicer의 "GUI 인쇄 버튼"이었다: 그건 Bambu의 x86 전용 바이너리 플러그인을 GUI가 로드해야 해서 ARM에서 막혔고, 오픈소스 aarch64 플러그인으로 교체해 풀었다. 하지만 그건 "GUI 경로" 얘기고, 스크립팅은 애초에 그 플러그인을 거치지 않는다.
정리: "완료되면 알림" 같은 자동화가 UI 앱에선 어려운 건, 앱이 사람 클릭을 전제한 UI 루프라서다. 프린터 자체는 UI 기기가 아니라 상태를 계속 방송하고 명령을 받는 서버라, 스크립트로 구독→조건→행동(알림·재인쇄·로깅)을 자유롭게 짤 수 있다. OS가 아니라 "UI냐 프로토콜이냐"의 문제다.
| 파일 | 역할 |
|---|---|
bambu_print.py | list / upload <파일> / print <파일> <AMS슬롯> — FTPS(curl) + MQTT(paho) |
watch_print.py | 완료 감시 → 데스크톱 팝업(notify-send) + 채팅 알림. 실패는 critical |
print.sh | 원커맨드: STL → 슬라이스(Orca CLI) → 업로드 → 인쇄 |
.env (600, git 밖) | 접속 정보: 호스트·액세스코드·시리얼 (비밀값이라 저장소 밖 · 여기 미포함) |