Building an Offline Arduino UNO Q Cyberdeck That Identifies Birdsong and Draws Vintage Field Notes
새소리를 분류하고 빈티지 현장 기록을 그리는 오프라인 Arduino UNO Q 사이버덱
B.L.O.O.M.은 Arduino UNO Q에서 사진·메모·새소리 분류를 오프라인으로 처리하는 자연 관찰용 사이버덱입니다. 사용자가 동기화를 선택하면 SQLite 기록을 GitHub PR로 보내고, Cloudinary API로 참고 이미지와 어울리는 현장 스케치를 생성합니다.
- 주제
에디터 노트
이 장치의 진짜 설계는 하드웨어가 아니라 경계에 있습니다. 오프라인에선 Gemma와 새소리 분류기를 보드 위에서 돌리고, 'Sync to Cloud'를 누르기 전까지 기기 밖으로 나가는 건 없습니다. 동기화도 GitHub PR을 거쳐 사람이 검토하고 병합해야만 사이트에 공개됩니다. 다만 이 글은 Cloudinary 계정이 올린 벤더 글이라 빈티지 스케치 생성 API가 주인공처럼 비춰집니다. 자동화조차 출판 앞에선 멈추는 선택이 핵심인데, 10달러짜리 빈티지 화장품 케이스의 감성이 없었다면 이 장치는 이렇게까지 회자되지 않았을 겁니다.
AI 요약
B.L.O.O.M.(Bridging Local Observations, Openly Mapped)은 야외에서 자연 관찰 기록을 남기는 오프라인 사이버덱입니다. Arduino UNO Q 4GB 모델에 카메라, 마이크, HDMI 화면, USB 허브와 키보드를 연결하고, 빈티지 화장품 케이스에 넣었습니다. 장치 안에서 사진을 찍고, 화면에 스케치하고, Gemma 기반 로컬 언어 모델에 관찰 내용을 물어볼 수 있습니다. 기록은 SQLite에 저장되며, 사용자가 ‘Sync to Cloud’를 누르기 전까지 장치 밖으로 나가지 않습니다.
장치와 로컬 앱
앱은 Arduino App Lab의 세 가지 brick으로 구성합니다. Web UI brick은 카메라와 스케치 인터페이스를 제공하고, LLM brick은 보드에 내려받은 Gemma 모델을 실행합니다. Audio Classification brick은 새소리를 분류합니다. 화면은 Debian에서 Chromium 키오스크 모드로 띄웁니다. 기본 Web UI brick이 팝업을 다른 창 뒤에 여는 문제를 해결하려고, 로컬 서버가 준비될 때까지 기다린 뒤 --kiosk와 --app 옵션으로 Chromium을 실행하는 작은 커스텀 brick을 추가했습니다.
새소리 모델은 Edge Impulse 커뮤니티 갤러리의 분류기를 바탕으로 다시 학습했습니다. 기존 데이터에 클래스 불균형과 녹음 부족 문제가 있어 쓸 수 없는 클립을 제거하고, xeno-canto에서 Blue Jay, Mourning Dove, Northern Cardinal 녹음을 종별로 최소 20개씩 가져왔습니다. 파일을 WAV로 변환하고 라벨을 붙인 뒤 업로드했으며, 학습·테스트 데이터를 재분배해 모델을 재학습했습니다. 새소리를 포착하면 버튼을 눌러 분류하고, 결과를 현장 기록에 저장합니다. 모델은 공개되어 있어 지역 종에 맞게 복제하고 데이터를 추가할 수 있습니다.
오프라인 기록을 웹사이트에 공유하는 과정
동기화할 때 장치는 SQLite에서 기록 ID, 시각, 사진, 스케치, AI 메모, 위치, 날씨와 서식지 정보를 읽습니다. 사진과 스케치는 Base64로 인코딩해 하나의 JSON 요청에 담고, Netlify Function으로 전송합니다. 이 함수는 데이터베이스에 직접 기록하지 않습니다. Octokit으로 field-notes 저장소에 새 브랜치와 Pull Request(PR)를 만들고, 위치·날씨·서식지 정보를 본문에 넣습니다. 관리자가 PR을 검토하고 병합하면 Astro 사이트를 다시 빌드해 기록을 공개합니다. 자동 업로드 대신 사람의 검토 단계를 두는 구조입니다.
Cloudinary로 빈티지 스케치 생성
PR을 열기 전에 Netlify Function은 기록 문장을 Cloudinary의 image_to_image API에 전달합니다. 모델은 nano-banana의 premium 등급을 지정하고, 기존 현장 스케치의 URL을 참고 이미지로 함께 보냅니다. 프롬프트는 기록에서 무엇을 그릴지 정하고, 참고 이미지는 결과의 빈티지 현장 노트 스타일을 맞춥니다. 생성 이미지는 managed_asset 대상으로 지정해 field-notes/<slug>-ai라는 예측 가능한 ID로 Cloudinary 라이브러리에 저장합니다. 이후 일반 Cloudinary 에셋처럼 포맷·품질 자동 선택과 크기 조정을 적용합니다.
API 인증 정보인 Cloudinary 계정명, 키와 비밀 키는 Netlify 환경 변수에 보관하며 장치에는 넣지 않습니다. 구현은 서버리스 함수에서 보내는 fetch 요청 한 번을 중심으로 구성하고 별도 SDK는 사용하지 않습니다. 글은 Cloudinary 이미지 생성 API를 무료 월간 제공량으로 이용한다고 설명합니다.
조립과 확장
케이스는 eBay에서 약 10달러에 구한 빈티지 Lancôme 기차형 화장품 케이스를 재사용했습니다. 카메라 렌즈 구멍을 내고 재활용 폼과 천으로 선반을 만들었으며, USB 허브와 화면을 고정했습니다. 폼과 천은 열을 가둘 수 있어 보드 주변 통풍을 고려해야 한다고 안내합니다. 전체 배선과 조립 과정은 Hackster의 별도 빌드 문서에 있습니다. 장치를 만들고 지역 새소리 데이터로 모델을 다시 학습하거나, 공유 사이트에 관찰 기록을 보태는 방식도 제안합니다.
원문: dev.to / 번역·요약: Trawling