Lisp와 M5Stack으로 프라이버시 우선 디지털 사진 프레임 만들기
대부분의 소비자 전자제품이 클라우드 구독과 개인 데이터 제공을 요구하는 시대에, 가족 추억을 간단하고 사적으로 공유하고자 하는 욕구는 놀라울 정도로 드뭅니다. 상업용 디지털 사진 프레임은 종종 높은 가격과 필수 계정을 요구하며, 이는 개인 사진이 인터넷에 유출되거나 AI 모델 학습에 사용될 위험을 안고 있습니다.
이를 해결하기 위해 개발자 chrisjj는 M5Stack Tab5와 uLisp를 사용해 맞춤형 디지털 사진 프레임을 만들었습니다. 로직을 Lisp 기반 펌웨어로 옮기고 이미지를 개인 서버에 호스팅함으로써, 이 프로젝트는 고해상도 디스플레이와 완전한 데이터 주권 사이의 균형을 이룹니다.
하드웨어 및 디스플레이 사양
이 프로젝트는 M5Stack Tab5를 중심으로 하며, 1280 x 720의 고해상도 디스플레이를 갖추고 있습니다. 이는 와이드스크린 16:9 비율과 뛰어난 색 재현을 제공해, 고가의 상업용 프레임에 대한 실용적인 대안이 됩니다.
이미지를 처리하기 위해 저자는 uLisp용 맞춤형 GIF 디코드/인코드 확장을 사용했습니다. 하드웨어가 메모리와 처리 능력에 제한이 있기 때문에 사진은 75% 디퓨전 디더링을 적용한 256색 GIF로 포맷됩니다. 이 과정은 보통 포토샵이나 온라인 변환 도구를 통해 이루어지며, 일반 사진 크기를 약 500KB 수준으로 유지하면서 원본과 거의 구분되지 않을 정도의 시각적 품질을 보장합니다.
시스템 아키텍처
사진 프레임은 원격 웹사이트에서 이미지 목록을 가져와 순환 재생하는 네트워크 클라이언트로 동작합니다. 이 아키텍처를 통해 소유자는 사진 갤러리를 원격으로 업데이트하고, 복잡한 앱 생태계 없이도 친구와 가족에게 업로드 권한을 부여할 수 있습니다.
백엔드
이미지는 www.wackyimage.com에서 간단한 HTTP 콘텐츠 관리 시스템(CMS)으로 제공됩니다. 사이트는 이미지 태그 목록을 포함한 보호된 페이지로 구성됩니다:
<body>
<p>Insert photos here! GIF format, image size 1280 x 720.</p>
<p><img src="/pictures/k58/sleepycat.gif" alt="SleepyCat.gif" /></p>
<p><img src="/pictures/k58/wedding.gif" alt="Wedding.gif" /></p>
</body>
Lisp 구현
펌웨어는 uLisp로 작성되었으며, 연결 → 수집 → 표시의 선형 실행 흐름을 따릅니다.
1. 네트워크 연결
디바이스는 wifi-connect 함수를 사용해 로컬 Wi‑Fi 네트워크에 연결을 초기화하고, 핸드셰이크가 완료되었는지 확인하기 위해 5초 지연을 둡니다:
(wifi-connect "Mynetwork" "mypassword")
(delay 5000)
2. 이미지 목록 가져오기
collect-pics-list 함수는 HTTP 요청을 처리합니다. 보안을 위해 로그인 쿠키를 사용하고, skip-headers 헬퍼 함수를 이용해 HTTP 응답 헤더를 건너뛰고 HTML 본문에 도달합니다.
(defun collect-pics-list nil
(with-client (s "www.wackyimage.com" 80)
(format s "GET /show?K5A HTTP/1.0~a~%" #\Return)
(format s "Host: www.wackyimage.com~a~%" #\Return)
(format s "Cookie: LOGIN=WI1NDlkNmE4YzBmZmE4MTQyYzNjODk2YzRkZDEwMiIp~a~%" #\Return)
(format s "Connection: close~a~%" #\Return)
(format s "~a~%" #\Return)
(skip-headers s)
(collect-pics s))))
3. 파싱 및 표시
collect-pics 함수는 HTML에서 특정 디렉터리 패턴(/k58/)을 검색해 파일명을 추출합니다. 목록이 완성되면 display-pic이 개별 GIF 파일을 가져와 스트림을 decode-gif 함수에 전달해 M5Stack 화면에 렌더링합니다.
메인 실행 루프
전체 과정은 회전 로직을 관리하는 photo-frame 함수에 감싸져 있습니다. 존재하지 않는 인덱스에 접근하지 않도록 사용 가능한 사진 수를 추적하고, 전환 속도를 제어하기 위해 설정 가능한 *minutes* 변수를 사용합니다.
(defun photo-frame ()
(wifi-connect "Mynetwork" "mypassword")
(delay 5000)
(let ((nextpic 0) (lastpics 0))
(loop
(let* ((pics (collect-pics-list))
(npics (length pics)))
(when (> npics lastpics)
(setq nextpic lastpics)
(setq lastpics npics))
(display-pic (nth (mod nextpic npics) pics)))
(incf nextpic)
(delay (* 1000 60 *minutes*)))))
디바이스가 독립형 기기로 동작하도록 저자는 (save-image 'photo-frame)을 통해 uLisp의 resetautorun 기능을 사용하여 전원이 켜질 때 루프가 자동으로 시작되도록 합니다.
결론
Lisp의 유연성과 저렴한 M5Stack 하드웨어를 결합함으로써, 이 프로젝트는 침해적인 클라우드 서비스에 의존하지 않고도 기능적이고 고해상도의 가전제품을 만들 수 있음을 보여줍니다. 간단한 마이크로컨트롤러를 동적인 원격 업데이트가 가능한 갤러리로 변환하면서 개인 데이터에 대한 엄격한 통제를 유지합니다.