1. 오늘 한 것
① 보호자 대시보드 UI 구조 완성
┌────────────────────────────────────────────┐
│ 시계 (타이틀바, 어두운 배경) │
├────────┬────────┬────────┬─────────────────┤
│ ↻버튼 │ │ │ │
├────────┴────────┴────────┴─────────────────┤
│ 온도카드 │ 습도카드 │ 움직임카드 │ 낙상카드 │
├────────────────────────────────────────────┤
│ 채팅 로그창 │
├────────────────────────────────────────────┤
│ 입력창 │ 전송 │ 알림판 │ 기록조회 │경고해제│
└────────────────────────────────────────────┘
- 화면을 크게 4개 영역(타이틀바 시계, 센서 위젯 카드, 채팅 로그창, 하단 컨트롤 버튼)으로 분할하여 배치.
- 센서 위젯 카드는 STATIC 컨트롤을 서브클래싱(CardProc)하고, WM_PAINT 메시지를 오버라이딩하여 센서 상태(온도, 습도, 움직임, 낙상위험)에 따라 배경색과 폰트를 동적으로 렌더링.
LRESULT CALLBACK CardProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (msg == WM_PAINT) {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rc; GetClientRect(hWnd, &rc);
// 배경색 (센서 상태에 따라 다름)
HBRUSH hbr = CreateSolidBrush(cardBgColor(hWnd));
FillRect(hdc, &rc, hbr);
DeleteObject(hbr);
// 센서 이름 (작게, 상단)
SetTextColor(hdc, RGB(100,100,100));
DrawTextW(hdc, title, -1, &rcTitle, DT_CENTER|DT_SINGLELINE);
// 센서 값 (크게, 중앙)
SelectObject(hdc, g_hCardValFont);
SetTextColor(hdc, RGB(30,30,30));
DrawTextW(hdc, value, -1, &rcVal, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
EndPaint(hWnd, &ps);
return 0;
}
return CallWindowProc(g_origCard, hWnd, msg, wParam, lParam);
}
② 이벤트 기록 조회 팝업 구현 (ListView 적용)
발생일시 구분 상세 내용 조치 상태
----------------------------------------------------------------------
03/19 14:30 비상 SOS 호출 버튼 클릭 확인필요
03/19 11:20 경보 낙상위험 경보 (10cm 미만 100초) 확인필요
- 초기 EDIT 컨트롤 사용 시 한글 가변 폭 문제로 컬럼 정렬이 어긋나는 현상 발생.
HWND hList = CreateWindowExW(0, WC_LISTVIEWW, L"",
WS_CHILD|WS_VISIBLE|WS_BORDER|LVS_REPORT|LVS_SINGLESEL,
8, 8, 786, 390, hDlg, nullptr, nullptr, nullptr);
ListView_SetExtendedListViewStyle(hList,
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
// 컬럼 추가
LVCOLUMNW col = {};
col.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
col.fmt = LVCFMT_LEFT;
col.cx = 110; col.pszText = L"발생 일시";
ListView_InsertColumn(hList, 0, &col);
col.cx = 70; col.pszText = L"구분";
ListView_InsertColumn(hList, 1, &col);
col.cx = 430; col.pszText = L"상세 내용";
ListView_InsertColumn(hList, 2, &col);
col.cx = 110; col.pszText = L"조치 상태";
ListView_InsertColumn(hList, 3, &col);
- 이를 해결하기 위해 WC_LISTVIEWW 컨트롤(Report 스타일)을 도입. '발생 일시, 구분, 상세 내용, 조치 상태' 4개의 컬럼을 명확히 나누어 텍스트 길이에 상관없이 표 형태로 깔끔하게 출력하도록 개선.
③ 거주자 전용 직관적 UI 구현
- 알림판 분리: 보호자가 NOTICE: 접두사로 보낸 메시지만 필터링하여 노란 배경의 큰 글씨로 별도 표시. 어르신이 중요 메시지를 놓치지 않도록 일반 채팅과 시각적으로 분리.
- 커스텀 SOS 버튼: BS_OWNERDRAW 스타일과 WM_DRAWITEM을 활용해 붉은색 SOS 버튼을 직접 그림. 클릭 시(ODS_SELECTED) 색상이 더 어두워지게 처리하여 사용자에게 명확한 조작 피드백 제공.
// 버튼 생성
HWND g_hSosBtn = CreateWindowW(L"BUTTON", L"SOS",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_OWNERDRAW,
554, 372, 138, 50, hWnd, (HMENU)ID_SOS, nullptr, nullptr);
// WM_DRAWITEM
case WM_DRAWITEM: {
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
if (dis->CtlID == ID_SOS) {
COLORREF clr = (dis->itemState & ODS_SELECTED)
? RGB(140,10,10) // 눌렸을 때 더 어둡게
: RGB(210,30,30); // 기본 빨간색
HBRUSH hbr = CreateSolidBrush(clr);
FillRect(dis->hDC, &dis->rcItem, hbr);
DeleteObject(hbr);
SetTextColor(dis->hDC, RGB(255,255,255));
SetBkMode(dis->hDC, TRANSPARENT);
DrawTextW(dis->hDC, L"SOS", -1, &dis->rcItem,
DT_CENTER|DT_VCENTER|DT_SINGLELINE);
return TRUE;
}
}
④ 최종 기능 점검
| 기능 | 확인항목 | 결과 |
| 센서카드 | ↻ 눌렀을 때 CARD: 패킷 요청·수신·갱신 | ✅ |
| 채팅 | 양방향 전송, 발신자 표시, DB 저장 | ✅ |
| 알림판 | NOTICE: 접두사 → 거주자 노란 화면 표시 | ✅ |
| SOS(UI) | 거주자 버튼 → 보호자 점멸 + alert_logs | ✅ |
| SOS(HW) | 아두이노 스위치 3초 → 서버 → 보호자 | ✅ |
| 낙상주의 | 30초 → [WARN] 로그만 | ✅ |
| 낙상경보 | 100초 → 빨간 점멸 + 경고음 | ✅ |
| 고온경보 후 자동환기 | 임계값 초과 → FAN 가동 + alert_logs | ✅ |
| 무움직임 감지 후 자동알림 | 3분 무감지 → 경보 + alert_logs | ✅ |
| 기록조회 | 버튼 → HISTORY 패킷 → ListView 팝업 | ✅ |
| 경고해제 | 버튼 → 점멸 중단 + UI 정상 복원 | ✅ |
| LED 타임아웃 | 10초 후 자동 소등 | ✅ |
2. 문제 / 헷갈린 점
① 긴급 점멸 시 자식 컨트롤 증발 현상 (WM_ERASEBKGND의 함정)
- 상황: 낙상/SOS 발생 시 500ms 타이머로 배경을 붉게 점멸시키려 InvalidateRect(hWnd, nullptr, TRUE);를 호출했으나, 버튼과 로그창이 함께 깜빡이거나 사라짐.
- 해결: nullptr로 전체 영역을 무효화한 것이 원인. 점멸 영역을 상단으로 제한하고, 자식 컨트롤을 보호하는 RDW_NOCHILDREN 플래그를 조합하여 부드러운 배경 점멸을 구현.
// 점멸 시: 버튼 영역 제외 + 자식 컨트롤 보호
RECT flashRect = {0, 0, 710, 390};
RedrawWindow(hWnd, &flashRect, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_NOCHILDREN);
// 해제 시: 전체 복원
RedrawWindow(hWnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW);
② ID_HISTORY 버튼 버그
- 상황: '기록 조회(ID_HISTORY)' 버튼을 눌러도 반응이 없고 센서 위젯만 갱신됨.
- 원인 : 어느 순간 SendHistory() 대신 SendCardUpdate()가 들어간 갔음.기록조회 버튼을 누르면 카드만 갱신되고 있었음.
- 해결: WM_COMMAND 핸들러 작성 중 SendHistory()가 들어가야 할 자리에 SendCardUpdate() 코드가 잘못 복사되어 있었음. UI 배치와 로직 연결은 별개의 문제임을 깨달음.
// 수정 후: DB 조회용 빈 패킷 전송 구조 확립
void SendHistory() {
if (g_sock == INVALID_SOCKET) return;
NetPacket pkt; memset(&pkt, 0, sizeof(pkt));
pkt.type = PT_DB_QUERY;
pkt.payload.chat.message[0] = '\0'; // 빈 payload → 서버에서 history로 분기
sendPacket(pkt);
}
case ID_HISTORY: {
SendHistory();
break;
}
서버에서는 PT_DB_QUERY payload가 "CARD"면 센서카드 조회,
비어있으면 alert_logs 조회로 분기하는 구조.
③ 아두이노 긴급 경보 LED 점멸 무한 반복 버그
- 상황: 위험 이벤트로 아두이노 LED가 켜진 후, 경고 상황이 끝났는데도 영원히 깜빡임.
- 원인 : 원인은 setLedAlert(true)를 호출할 때 시작 시각을 기록하는 코드가 어느 순간 수정되면서 사라졌음.
- 해결: 타임아웃 체크 로직이 누락되어 있었음. 타이머 변수(ledAlertStart)를 추가하여 경보 시작 10초(LED_ALERT_TIMEOUT) 후에는 서버의 명시적 해제 명령이 없어도 하드웨어 단에서 자동 소등되도록 방어 코드 추가.
// 수정 전 - 타임아웃 체크 없음
if (ledAlert && (now - lastBlink >= BLINK_MS)) {
lastBlink = now;
blinkState = !blinkState;
digitalWrite(LED_PIN, blinkState ? HIGH : LOW);
}
// 수정 후 - 10초 후 자동 해제
if (ledAlert) {
if ((now - ledAlertStart) >= LED_ALERT_TIMEOUT) {
setLedAlert(false); // 자동 종료
} else if (now - lastBlink >= BLINK_MS) {
lastBlink = now;
blinkState = !blinkState;
digitalWrite(LED_PIN, blinkState ? HIGH : LOW);
}
}
3. 오늘 배운 핵심
- UI 개발의 본질은 '동작의 투명성'이다: 버튼이 화면에 예쁘게 떠 있는 것과 실제 기능이 올바른 이벤트 핸들러(WM_COMMAND)에 맵핑되어 있는지는 꼼꼼한 교차 검증이 필수적이다.
- 한글 UI 출력에는 ListView가 해답이다: 고정폭 폰트와 sprintf 서식만으로는 한글의 가변적인 렌더링 폭을 제어할 수 없다. 데이터를 표 형태로 정렬할 때는 반드시 ListView를 써야 한다.
- 안전장치는 다중으로 걸어라: 알람 해제는 소프트웨어(클라이언트 조작)뿐만 아니라, 하드웨어(아두이노) 자체 타이머를 통한 자동 해제 로직도 마련해야 예외 상황에 대처할 수 있다.
4. 다음 목표
- 오늘 점검한 최종 체크리스트(센서 연동, 알람 전송, DB 저장 등 10개 항목)를 바탕으로 포트폴리오용 시연 영상 촬영.
- 프로젝트 전체의 시스템 아키텍처, 구현 한계점, 그리고 향후 개선 방향(보안 강화 등)을 종합하여 최종 기술 발표 자료(PPT) 완성하기.
'포트폴리오 > [IoT 기반 피지컬 AI 교육일지]' 카테고리의 다른 글
| [ JAVA 기초 ] 자바 클래스 기본 구조 및 로봇 산업 취업 정보(31일차 기록) (0) | 2026.03.27 |
|---|---|
| [ C언어 IoT 프로젝트 ] #6 스마트홈 최종 발표 준비 및 회고 (30일차 기록) (0) | 2026.03.27 |
| [ C언어 IoT 프로젝트 ] #4 인코딩 에러수정, WinAPI 비동기 UI 개발 (28일차 기록) (0) | 2026.03.24 |
| [ C언어 IoT 프로젝트 ] #3 MySQL 연동 및 멀티스레드 TCP 소켓 구축(27일차 기록) (0) | 2026.03.24 |
| [ C언어 IoT 프로젝트 ] #2 스마트홈 센서 테스트 & 시리얼 통신 (26일차 기록) (0) | 2026.03.23 |