
구현한 공동현관의 일부 기능 - 인가된 태그 인식:

드디어 스마트홈 IoT 프로젝트가 끝났다! 기간은 고작 일주일이었지만, 정말 많은 것을 배우고 느낀 프로젝트였다.
이 프로젝트에서 나는 공동현관 기능을 맡았는데, 스테퍼 모터(28BYJ-48)로 슬라이딩 도어를 제어하고, RFID(MFRC522)로 태그를 인식하고, 초음파 센서로 장애물을 감지하는 부분을 담당했다. 그리고 Flask로 서버를 구축해서 PyQt6로 만든 대시보드와 HTTP 통신을 하는 부분도 구현했다.
아두이노(C++), Python(Flask, PyQt6), 그리고 RDS를 사용했고, 이 과정에서 예상하지 못한 문제들도 많았지만, 그만큼 배운 것도 정말 많았다. 그 이야기들을 나눠보려고 한다.
여러 개의 아두이노 보드가 Flask 서버로 시리얼 메시지를 계속 보내야 했다. 그런데 어떤 아두이노가 보낸 메시지인지, 그리고 그 메시지가 무엇을 의미하는지 구분할 방법이 필요했다. 특히 아두이노 보드 간의 통신도 필요했기에 중앙 제어 서버를 두는 걸 선택했다.
이 때문에 팀에서 함께 통신 프로토콜을 설계했는데, 메시지 형식의 기본 구조는 이렇게 했다:
[DEVICE_ID],[DATA_TYPE],[VALUE]
예를 들어 MAIN,RFID_VALID,12A34B56 이런 식이다. 각 아두이노의 ID, 어떤 센서의 데이터인지, 그리고 실제 값을 구분하는 것이다.
사실 실무에서는 바이트 개수를 기준으로 파싱한다고 하는데, 시간이 부족해서 빠르게 진행하기 위해 콤마를 구분자로 사용했다. 이건 나중에 개선하고 싶은 부분이다.
처음 계획은 이렇게 단순했다:
"두 프로세스가 같은 포트(시리얼 포트)를 공유할 수 있지 않을까?" 라는 뇌피셜로 시작했다(사실 타이트한 일정이라 제발 됐으면 좋겠다 라는 생각이었다)
하지만 당연하게도... 한 프로세스가 포트를 점유하면 다른 프로세스는 접근할 수 없었다. 😅
공동현관이 제대로 작동하려면 세 가지 센서가 조화롭게 움직여야 했다.
RFID 태그 인식:
스테퍼 모터로 도어 제어:
초음파 센서로 안전성 확보:
다만 아쉬웠던 점이 하나 있다. 지금은 태그의 UID를 그대로 대시보드에 표시하는데, 실제 서비스라면 사용자 이름이나 라벨이 나와야 할 텐데 시간 관계상 못했다. 다음엔 데이터베이스에서 태그 ID를 조회해서 실제 사용자 정보를 표시해야겠다.
처음엔 관제탑 역할의 Python 프로세스가 시리얼 통신만 담당했다. 하지만 포트 점유 문제가 터지면서 급하게 리팩토링이 필요했다.
해결책: Flask 서버로 전환
대시보드에서는 이제 이렇게 통신한다:
GET/POST http://localhost:5000/door/control
Flask 서버가 요청을 받으면 아두이노로 명령을 내려준다.
대시보드 상태 표시:
흥미로운 부분이었다. 공동현관이 열림 상태가 되면, 자동으로 엘리베이터도 호출되도록 했다. 이건 Flask 서버를 통해서 다른 아두이노 보드에 명령을 전달하는 식으로 구현했다.
프로젝트 초반, 나는 큰 실수를 했다. 아두이노 보드를 항상 노트북에 연결시켜 뒀다.
컴퓨터를 끄면 USB에 연결된 기기들도 전기가 끊긴다고 생각했다... 근데 그게 아니었다. 컴퓨터를 끄고 나서도 아두이노 보드는 계속 전원을 받고 있었다. 스테퍼 모터 드라이버, RFID 모듈까지 달아둔 상태로 말이다.
며칠 동안 계속 전원을 받던 보드의 온도가 올라가거나 하는 신호는 없었다. 그래서 과부하가 있다는 걸 전혀 몰랐다.
통합 테스트 전: 발표 전, 대시보드까지 합쳐서 통합 테스트를 하려고 보드를 켰는데... 먹통이 되어버렸다. 컴퓨터에서도 인식을 못 했다.
아두이노 내부 회로가 손상된 거다. 여러 개의 전자 부품들이 계속 전력을 소비하다 보니 보드 자체가 과부하를 견디지 못한 거다. (거의 일주일 동안 끊임없이 돌렸으니 그럴만도 하다...)
다행히: 팀 친구들이 여분의 아두이노 보드를 빌려줘서 발표는 무사히 진행할 수 있었다.
앞서 언급했던 포트 점유 문제로 돌아와서.
처음엔 "관제탑 Python 프로세스"와 "대시보드 Python 프로세스" 두 개를 동시에 실행하려고 했다. 근데 시리얼 포트는 한 번에 하나의 프로세스만 점유할 수 있다.
문제를 깨닫던 순간:
serial port is busy
이 에러 메시지를 본 순간 내 얼굴은 파랗게 질렸다. 🫠
해결 과정:
1. 급하게 관제탑 역할을 Flask 서버로 리팩토링
2. 대시보드는 Flask 서버와 HTTP 통신으로 변경
3. 아두이노는 Flask 서버에만 시리얼 연결
운이 좋았던 이유:
처음부터 프로토콜을 잘 정의해뒀기 때문에, 아두이노 쪽 코드는 수정할 필요가 거의 없었다. 대시보드에서 시리얼로 처리하던 것들을 그대로 서버에 보내면 됐다.
이 경험에서 설계가 얼마나 중요한지를 뼈저리게 깨달았다. 미리 잘 정의해둔 프로토콜 덕분에, 예상 밖의 문제가 터졌을 때도 빠르게 대응할 수 있었다.
지금: 12A34B56 같은 UID가 그대로 표시됨
개선: 데이터베이스에서 태그 ID를 조회해서 김철수 같은 실제 사용자명 표시
RDS를 이미 연결했으니 구현이 어렵지 않을 텐데, 시간이 부족했다. 다음 버전에서 꼭 개선해야겠다.
지금: 콤마를 구분자로 파싱 (DEVICE_ID,DATA_TYPE,VALUE)
실무: 바이트 개수를 기준으로 고정된 형식으로 파싱
바이트 기반 파싱이 더 안정적이고 효율적이라고 들었다. 이것도 다음 프로젝트의 과제로 삼아야겠다.
이건... 기술적 개선이라기보단 휴먼 에러 예방이다.
이 프로젝트에서 배운 가장 중요한 교훈은 이거다:
"좋은 설계가 있으면, 예상 밖의 문제가 터져도 빨리 해결할 수 있다."
프로토콜을 미리 잘 정의했기 때문에, 아키텍처를 완전히 바꿔야 할 상황이 와도 빠르게 대응할 수 있었다. 반대로 설계를 대충 했다면, 발표 전날의 그 난리는 정말 감당할 수 없었을 거다.
일주일이라는 짧은 기간이었지만, 정말 밀도 있는 시간이었다.
이 프로젝트에서 배운 가장 큰 것:
다음 IoT 프로젝트를 할 땐:
1. 더 견고한 설계로 시작하기
2. 하드웨어 소중히 다루기 (특히 보드...)
3. 충분한 테스트 시간 확보하기
4. 배운 교훈들을 적용해서 더 완성도 있는 프로젝트 만들기
이 경험이 앞으로의 IoT 프로젝트에 큰 밑거름이 될 거라고 믿는다. 그리고 그때는 아두이노 보드도 편안하게 쉬게 해줄 거다.