아두이노를 하다보면 그래픽으로 무언가 표시하고 싶을때가 있습니다.
제 이전 포스트에서 차량정보를 뿌려주는 데시보드 제작에 대한 글을 상세하게 올렸는데 그후에 확장, 업그래이드(?)를 하여 최종적으로 8개의 데이터를 표시하게 하였습니다.
이 데시보드는 8개 OLED 디스플레이 액정에 각기 다른 정보를 그래픽으로 표시하고 있는데, 대부분 다 숫자로 표시해도 되지만 아날로그, 디지털 게이지 형태로 표시해 주면 완성도와 시인성에서도 개선이 되기 때문에 게이지 디자인에 상당히 신경을 써서 디자인 하게 되었습니다.
약간의 수학적 지식이 필요했던 아날로그 게이지 디자인은 외국의 어느 개발자가 공개해 놓은 코드를 그대로 가져다 썻는데 코드 전체가 이해되지 않아 나중에 다시 코드를 짜서 사용하게 되었습니다.
아날로그 게이지 구현은 마치 시계를 디자인하는 것과 똑같습니다.
표시할 변수값에 따라서 게이지 바늘이 시계바늘처럼 정확하게 그려져야 하는데 이부분은 삼각함수 공식이 들어가기 때문에 조금 어려웠습니다.
(제가 수포자라서요 TT)
이 포스트에서 설명하는 게이지 구현은 128*64픽셀의 OLED 디스플레이 액정을 사용하였고, 그래픽은 u8glib 라이브러리를 사용하였습니다.
먼저 디자인된 아날로그 게이지는 바늘핀이 180도 움직이고 디지털값이 표현되는 게이지입니다.
이 게이지의 스케치는 다음과 같습니다.
#include "U8glib.h" U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // 게이지를 그리기 위해 필요한 변수들 int px1=64, py1=64; // 바늘침의 시작 위치 int px2, py2; // 바늘침의 그려지는 위치 int circle, pin; // 게이지 큰원, 바늘침 길이 int angle; // 변수값에 따라 바늘침이 움직여야 하는 각도값 double rad; // px2, py2 위치값을 알아내기 위한 라디언값 // 데이터를 처리하기 위한 변수들 (필요시 추가로 선언) int val; void setup(void) { circle = 63; // px1, py1을 기준으로 큰 원을 63으로 설정 pin = 53; // 바늘침 크기 설정 } void Disp() { angle = map(val, 0, 3000, 180, 360); // 180~360구간만 사용 (****중요) rad = angle * 3.14 / 180; // 각도를 라디안값으로 변환 px2 = px1+(cos(rad)*pin); // 바늘침 x좌표 위치 계산 py2 = py1+(sin(rad)*pin); // 바늘침 y좌표 위치 계산 u8g.firstPage(); do { // 겉에 가장 큰 원 2겹으로 u8g.drawCircle(px1, py1, circle, U8G_DRAW_UPPER_RIGHT); u8g.drawCircle(px1, py1, circle, U8G_DRAW_UPPER_LEFT); u8g.drawCircle(px1, py1, circle-2, U8G_DRAW_UPPER_RIGHT); u8g.drawCircle(px1, py1, circle-2, U8G_DRAW_UPPER_LEFT); u8g.setFont(u8g_font_profont11r); // 6*10크기의 폰트를 사용 u8g.drawStr(56, 24, "VAL"); u8g.drawStr(6, 64, "0"); u8g.drawStr(15, 32, "."); u8g.drawStr(36, 19, "1"); u8g.drawStr(62, 9, "."); u8g.drawStr(88, 19, "2"); u8g.drawStr(108, 32, "."); u8g.drawStr(118, 64, "3"); u8g.drawLine(px1, py1, px2, py2); // 바늘침 그리기 u8g.drawDisc(px1, py1, 5, U8G_DRAW_UPPER_RIGHT); // 바늘침 시작점 반원 그리기 u8g.drawDisc(px1, py1, 5, U8G_DRAW_UPPER_LEFT); // 값 표시 u8g.setFont(u8g_font_profont29r); if (val<1000) { u8g.setPrintPos(34,52); // 변수값이 1000보다 작을 경우 숫자 0을 찍어주고 숫자값 표시되는 위치를 조정 u8g.print("0"); u8g.setPrintPos(50,52); } else { u8g.setPrintPos(34,52); } u8g.print(val); } while( u8g.nextPage() ); } void loop(void) { val = random(100, 3000); Disp(); delay(500); } |
선언된 변수값과 다지인 레이아웃을 같이 보시면 좀더 이해가 쉬울것 같습니다.
바늘침의 중심점은 px1, py1으로 고정해 놓고 값에 따라 움직일 px2, py2 위치값을 angle 각도값에 따라 구하는 것이 핵심이 되겠습니다.
즉 스케치 빨간색 부분의 코드가 이부분인데요, 이 코드를 좀더 자세히 설명해 드리면
angle = map(val, 0, 3000, 180, 360); // 180~360구간만 사용 (****중요) rad = angle * 3.14 / 180; // 각도를 라디안값으로 변환 px2 = px1+(cos(rad)*pin); // 바늘침 x좌표 위치 계산 py2 = py1+(sin(rad)*pin); // 바늘침 y좌표 위치 계산 |
먼저 loop()에서 val값을 100~3000까지 랜덤값을 넣어주고 있습니다.
(여러분들이 실제로 구현하실때는 게이지의 실제 값이 되겠죠)
angle = map(val, 0, 3000, 180, 360);
이 코드는 px2, py2의 바늘침 각도 값을 구하는 코드입니다.
map()함수를 사용하여 val값에 따라서 180~360사이의 각도값을 구하고 있습니다.
(혹시 map함수에 대해 이해가 없으시다면 먼저 map함수에 대해 검색해 보시고 읽어 주세요)
이런식으로 각도값을 구하는 것은 하단의 그림과 같이 게이지가 그려지는 구간이 삼각함수로 px2, py2의 좌표값을 구할때 180~360도 구간에 해당하기 때문입니다. (설명이 어렵네요^^;;;)
다음 3줄의 코드는 각도로 원상에서 좌표값을 구하기 위해 라디언값을 구한 후 삼각함수 식을 통해 최종 좌표값을 구하는 코드입니다.
rad = angle * 3.14 / 180; // 각도를 라디안값으로 변환
px2 = px1+(cos(rad)*pin); // 바늘침 x좌표 위치 계산
py2 = py1+(sin(rad)*pin); // 바늘침 y좌표 위치 계산
공식만 적용한거기 때문에 공식 자체에 대해서 궁금하신 분들은 다른 자료를 참고해 보시기 바랍니다.
이 3줄의 코드가 제일 중요하고 이외 코드는 실제 화면에 뿌리기 위한 그래픽 처리함수 이기 때문에 따로 설명 안드려도 될것 같습니다.
참고로 u8glib의 레퍼런스와 사용가능한 폰트리스트 링크 걸어드립니다.
https://github.com/olikraus/u8glib/wiki/userreference
https://github.com/olikraus/u8glib/wiki/fontsize
이상 아두이노 아날로그 게이지 디자인에 대해 도움이 되실까하여 글 적었습니다.
다음 글에서는 약간 응용하여 디자인의 변형을 주는것을 적어보도록 하겠습니다.
'아두이노' 카테고리의 다른 글
아두이노 디지털 게이지 디자인 (1) | 2019.09.11 |
---|---|
아두이노 아날로그 게이지 디자인 응용 (0) | 2019.09.11 |
아두이노로 GPS 신호를 직접 처리(분석) 하기 (3) | 2019.03.10 |
아두이노로 자동차 대시보드를 완성하다 - 3편 (10) | 2019.02.09 |
아두이노로 자동차 대시보드를 완성하다 - 2편 (5) | 2019.02.09 |