화면은 켰는데 글자가 안 된다
1편 에서 좌표 변환으로 픽셀을 제자리에 보내는 데까지 성공했다. 이제 글자를 찍을 차례였다. 그런데 한글을 출력하니 획이 잘리고 깨졌다.
이 전광판이 띄워야 하는 문구는 전부 한글이다. "안전제일", "더위 조심", "안전미흡 식별시 관리자에게 보고" 같은 것들. 글자가 안 되면 프로젝트 자체가 성립하지 않는다.
왜 깨지는가

아두이노 계열의 비트맵 폰트 라이브러리는 대부분 영문 기준으로 만들어져 있다. 알파벳은 획이 적어서 5×7이나 6×8 같은 작은 격자에도 들어간다.
패널 높이가 32px이고 그중 글자에 쓸 수 있는 영역은 24px 남짓이었다. 이 크기에서 기존 폰트로는 답이 없었다.
폰트를 찾는 대신 에디터를 만들었다
선택지는 두 가지였다. 한글을 지원하는 비트맵 폰트를 찾아 이식하거나, 필요한 글자만 직접 찍거나.
후자를 택했다. 이 전광판이 띄우는 문구는 정해져 있다. 시계 숫자, 날씨 단어, 안전 수칙 6단계. 전체 한글 음절 11,172자가 필요한 게 아니라 쓰는 글자만 있으면 된다.
그래서 브라우저에서 도는 HTML5 Canvas 픽셀 에디터를 만들었다. 격자 위에 마우스로 픽셀을 찍고, 결과를 C++ 배열로 뽑아내는 도구다.
4배로 그려서 줄인다
픽셀을 하나씩 손으로 찍는 것만으로는 부족했다. 글자 모양의 기준이 필요했다. 그래서 실제 폰트를 캔버스에 렌더링한 뒤 그걸 격자로 내리는 방식을 썼는데, 여기서 바로 줄이면 획이 끊어졌다.
| 기법 | 내용 |
|---|---|
| 4× 슈퍼샘플링 | 96×96 오프스크린 캔버스에 먼저 렌더링한 뒤 24×24로 줄인다 |
| Dual-threshold 획 보정 | 단일 임계값으로 이진화하면 얇은 획이 통째로 사라진다. 두 단계 임계값으로 획 끊김을 막는다 |
| 404px 표준 크기 고정 | 24×24 픽셀 가이드 기준으로 글자 크기를 404px에 고정해 도안 간 굵기를 통일 |
슈퍼샘플링은 원래 그래픽스에서 계단 현상을 줄일 때 쓰는 기법이다. 큰 해상도로 그린 뒤 축소하면 픽셀 하나에 여러 샘플의 평균이 담긴다. 여기서는 그 평균값이 "이 픽셀에 획이 얼마나 걸쳐 있는가"의 척도가 된다. 그 값을 두 단계로 나눠 판정하니 가는 획이 살아났다.
찍은 다음이 더 중요했다
도안을 53개 만들었다. 한글 단어와 아이콘이다. 이걸 손으로 C++ 배열에 옮겨 적는 건 현실적이지 않다. 하나 고칠 때마다 다시 옮겨야 하기 때문이다.
update_all_pixel_data.py 스크립트를 만들어 53개 전 도안의 16비트 RGB565 C++ 배열과 JSON을 자동 생성하도록 했다. 에디터에서 고치고 스크립트 한 번 돌리면 펌웨어에 반영된다.
Adafruit_GFX 는 색을 16비트로 다룬다. R 5비트, G 6비트, B 5비트다. 사람 눈이 녹색에 가장 민감해서 G에 한 비트를 더 준다. 24비트 트루컬러 대비 메모리가 3분의 2로 줄고, 마이크로컨트롤러의 좁은 플래시에서는 이 차이가 크다.이 결정이 남긴 것
"라이브러리를 찾는다"에서 "필요한 것만 직접 만든다"로 방향을 튼 판단이었다. 결과적으로 세 가지를 얻었다.
- 외부 폰트 라이브러리 의존이 0이 됐다. 플래시에 들어가는 건 실제로 쓰는 53개 도안뿐이다. 전체 한글 폰트를 넣었다면 용량이 감당 안 됐을 것이다.
- 글자 모양을 내가 통제한다. 24×24 격자에서 어떤 획을 살리고 어떤 획을 버릴지는 결국 사람이 판단해야 하는 문제였다. 자동 변환에만 맡겼다면 계속 아쉬운 결과를 받았을 것이다.
- 도안을 고치는 비용이 거의 0이다. 브라우저에서 찍고 스크립트 한 번 돌리면 끝난다. 이 왕복이 짧아진 덕분에 실제로 여러 번 고쳤다.
반대로 대가도 있다. 이 에디터 자체가 유지보수 대상이 됐다. 새 문구를 추가하려면 도안을 찍어야 하고, 그건 폰트를 쓰는 프로젝트에서는 없는 작업이다. 문구 종류가 수십 개 수준이니까 성립하는 선택이었다.
2편 정리, 그리고 다음
여기까지가 화면에 원하는 한글을 띄우기까지의 이야기다. 폰트 문제를 도구 제작으로 우회했고, 그 도구가 만든 데이터가 자동으로 펌웨어에 들어가는 파이프라인까지 만들었다.
다음 편은 화면이 살아 움직이기 시작하면서 생긴 문제들이다. 기상청 API를 부르면 화면이 최대 15초씩 멈췄고, 부팅 초반에는 배경에 푸르스름한 잔상이 남았다. 각각 FreeRTOS 코어 분리와 가로 스위핑 연출로 해결했다.
그리고 4편에서 그 잔상 해결책이 사실 원인을 못 찾은 우회책이었다는 걸 알게 된다.