Android 앱이 모니터링, 제어(오차보정), 데이터소스 전환(LOCAL/FIREBASE/SERVER)을 담당하고, 서버/장비/대시보드는 앱 기능을 확장하는 형태로 설계했다.
- LOCAL: SQLite
- FIREBASE: Firestore
- SERVER: FastAPI + Neon(PostgreSQL)
- 확장: WebSocket 관리자 관제(
/admin), Grafana 운영 대시보드
아두이노가 RFID 태깅으로 이벤트를 발생시키면 서버(FastAPI)가 이를 수신해 Neon(PostgreSQL)에 기록한다. 서버는 LSTM, SPC, CUSUM, R2R로 오차를 감지하고 보정하며, 이 과정은 WebSocket(/admin)으로 실시간 스트리밍되고 Grafana에서 지표로 집계된다. Android 앱은 LOCAL(SQLite), FIREBASE(Firestore), SERVER(REST) 모드로 이를 조회하고 모니터링한다.
SmartCan은 서버와 하드웨어가 있는 프로젝트지만, 사용자가 실제로 조작하고 확인하는 중심은 Android 앱이다.
앱이 제공하는 기능:
- 모드 전환(LOCAL/FIREBASE/SERVER): 1·2·3단계를 앱 한 곳에서 확인할 수 있다.
- 실시간 모니터링 UI: 알람, 상태, SPC 결과를 앱 화면에서 즉시 확인한다.
- 제어(오차보정 버튼): 사용자가 앱에서 보정 요청하면 장비 LED가 빨강(ERR)에서 초록(OK)으로 바뀌며 효과를 눈으로 확인한다.
- Android 앱이 SQLite로 저장하고 조회한다.
- 오프라인에서도 동작한다.
- Android 앱이 Firestore에 저장하고 조회하며 실시간 갱신된다.
- 앱 설정으로 LOCAL과 FIREBASE 간에 전환할 수 있다.
- Android 앱이 REST로 서버와 통신한다.
- 서버는 Neon(PostgreSQL)에 저장하고 조회한다.
- 서버는 명령어만으로 독립적으로 실행할 수 있다.
발표와 시연은 다음 3개 화면 중심으로 진행한다.
- 현재 SKU와 라인 상태
- 최근 알람 리스트(WARN/ERR)
- 최근 충진 결과(목표/실측, OK/ERR)
- 오차보정 버튼
- 목표량/레시피(SKU) 설정
- DataSource: LOCAL / FIREBASE / SERVER
- 현재 모드 표시
SmartCan의 차별점은 기록과 조회가 아니라 충진 오차를 감지하고 자동으로 보정 루프를 돈다는 점이다.
- LSTM: SKU와 이력을 기반으로 밸브 시간을 추정한다.
- SPC(관리도): 공정 분산과 허용오차를 기반으로 이상을 판단한다.
- CUSUM: 누적 변화로 드리프트를 조기에 감지한다.
- R2R(Run-to-Run): 직전 결과를 반영해 다음 사이클 보정값을 적용한다.
- 알람: 이상 감지 시 DB에 저장하고 관리자에게 즉시 푸시하며 Grafana에서 추이와 빈도를 확인한다.
- (장비/UNO) RFID 태깅으로 SKU를 결정하고
can_in이벤트를 전송한다. - (브리지/esp_bridge.py) 시리얼을 읽어
line1/event/can_in을 MQTT에 발행한다. - (서버/FastAPI)
can_in을 수신하고 LSTM/R2R로 다음 밸브 시간을 계산해 Neon DB에 기록하고 WebSocket으로 푸시한다. - (장비/UNO) 충진을 수행하고
fill_result이벤트를 전송한다(실제량/목표량/OK·ERR 포함). - (서버/FastAPI)
fill_result를 수신하고 DB에 기록한 뒤 SPC/CUSUM으로 이상을 감지해 알람을 생성하고 WebSocket으로 푸시한다. - (Android 앱) 사용자가 오차보정 버튼을 클릭하면 서버로 보정 요청을 보낸다.
- (서버/FastAPI) 보정값을 산출하고 기록한 뒤
CORR명령을 발행하고 WebSocket으로 푸시한다. - (장비/UNO)
CORR을 수신하고 LED를 빨강(ERR)에서 초록(OK)으로 전환한다. - (관리자)
/admin에서 WebSocket 실시간 로그를 확인하고 Grafana에서 알람과 지표 추이를 확인한다.
smartcan/
arduino/ # UNO 스케치 (RFID/밸브/LED/LCD/7세그)
backend/ # FastAPI 서버 (3단계)
dashboard-app/ # Android 앱 (LOCAL/FIREBASE/SERVER 전환)
infra/ # 로컬 개발용 docker (Postgres/MQTT/Grafana)
esp_bridge.py # UNO 시리얼 ↔ MQTT 브리지
README.md
cd backend
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Health check: GET /health
API 문서: GET /docs
backend/.env.example을 backend/.env로 복사하고 수정한다.
#SQLite
#FIREBASE
DATABASE_URL=postgresql://<NEON_USER>:<NEON_PASSWORD>@<NEON_HOST>/<NEON_DB>?sslmode=require
SERVER_PORT=8000
# MQTT
MQTT_BROKER_HOST=localhost
MQTT_BROKER_PORT=1883주의: .env는 커밋하지 않는다.
dashboard-app/을 Android Studio로 열고 Run한다.
dashboard-app/app/build.gradle.kts에서 DATA_SOURCE 값을 변경한다.
"\"LOCAL\""// 1단계: SQLite"\"FIREBASE\""// 2단계: Firebase(Firestore)"\"SERVER\""// 3단계: FastAPI + PostgreSQL(Neon)
변경 후 반드시 상단의 Sync Now를 클릭해야 적용된다.
SERVER 모드 주소
- 에뮬레이터:
http://10.0.2.2:8000(호스트 PC의 localhost) - 실기기(같은 Wi-Fi):
http://<PC 로컬 IP>:8000
FIREBASE 모드
google-services.json과 Firestore 설정이 필요하다.
앱을 삭제하고 재설치하거나 앱 데이터를 삭제한다.
Firebase Console에서 테스트 컬렉션을 삭제하거나 테스트 프로젝트를 분리한다.
Neon Branch를 새로 생성하거나 스키마를 초기화한다(데이터 전체 삭제 주의).
cd infra
docker compose down -v
docker compose up -dBase URL: http://<server-host>:8000
기본 엔드포인트:
GET /healthGET /docsGET /adminWS /ws/admin
API 예시:
GET/POST /api/v1/alarmsGET/POST /api/v1/spc/statesGET/POST /api/v1/cyclesPOST /api/v1/control/fillPOST /api/v1/control/corr(앱 오차보정 버튼과 연결)
- RFID UID를 SKU에 매핑한다(COKE_355 / CIDER_500).
- Changeover 감지 시 노랑 LED를 표시한다.
- Changeover 직후 다음 태깅에서 BAD FILL이 발생하면 빨강 LED를 표시하고
fill_result(ERR)을 전송한다. - 앱에서 오차보정 버튼을 누르면 서버가
CORR을 발행하고 UNO LED가 빨강에서 초록으로 전환된다.
데이터 소스: Neon(PostgreSQL)
패널:
- 라인 상태
- 알람 빈도
- 오차율
- 모델 지표
권한:
- Folder:
Admin Dashboard - Teams:
ops-admin(Edit),ops-viewer(View)
- 앱 Settings에서 LOCAL ↔ FIREBASE ↔ SERVER를 전환해 1·2·3단계를 확인한다.
- Dashboard에서 알람과 상태를 확인한다.
- BAD FILL을 유도해 빨강 LED를 표시한 뒤 앱에서 오차보정 버튼을 누르면 LED가 빨강에서 초록으로 전환되는 것을 확인한다.
- 관리자
/admin에서 실시간 로그를 확인하고 Grafana 지표로 운영까지 확장해 보여준다.