/*
* 조이스틱 움직임 OLED 그래프 표현
*
* 사용 라이브러리 : u8glib
* 라이브러리 링크 : https://github.com/olikraus/u8glib
*
* 개선사항:
* 1. #define 대신 const/constexpr 사용하여 타입 세이프티 강화
* 2. 변수명 일관성 유지 (상수는 대문자 스네이크 케이스, 변수는 스네이크 케이스)
* 3. OLED 초기화 및 업데이트 과정을 별도 함수로 분리
*/
#include <U8glib.h>
// 화면 크기 상수
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr uint8_t CENTER_X = SCREEN_WIDTH / 2;
constexpr uint8_t CENTER_Y = SCREEN_HEIGHT / 2;
// 그래프 관련 상수
constexpr uint8_t X_BAR_HEIGHT = 10; // X축 막대 그래프 높이
constexpr uint8_t X_BAR_Y_POS = 20; // X축 막대 그래프 Y위치
constexpr uint8_t Y_BAR_WIDTH = 10; // Y축 막대 그래프 너비
constexpr uint8_t Y_BAR_X_POS = 90; // Y축 막대 그래프 X위치
// 라벨 위치 상수
constexpr uint8_t LABEL_MINUS_X_X = 50; // -x 라벨 X 좌표
constexpr uint8_t LABEL_MINUS_X_Y = 10; // -x 라벨 Y 좌표
constexpr uint8_t LABEL_PLUS_X_X = 70; // +x 라벨 X 좌표
constexpr uint8_t LABEL_PLUS_X_Y = 10; // +x 라벨 Y 좌표
constexpr uint8_t LABEL_MINUS_Y_X = 40; // -y 라벨 X 좌표
constexpr uint8_t LABEL_MINUS_Y_Y = 42; // -y 라벨 Y 좌표
constexpr uint8_t LABEL_PLUS_Y_X = 40; // +y 라벨 X 좌표
constexpr uint8_t LABEL_PLUS_Y_Y = 30; // +y 라벨 Y 좌표
// 값 표시 위치 상수
constexpr uint8_t X_VALUE_X = 5; // X값 표시 X 좌표
constexpr uint8_t X_VALUE_Y = 10; // X값 표시 Y 좌표
constexpr uint8_t Y_VALUE_X = 97; // Y값 표시 X 좌표
constexpr uint8_t Y_VALUE_Y = 10; // Y값 표시 Y 좌표
// 조이스틱 매핑 상수
constexpr uint16_t JOY_MIN = 0;
constexpr uint16_t JOY_MAX = 1023;
constexpr uint16_t JOY_CENTER = 512;
constexpr int8_t X_MAP_MIN = -64;
constexpr int8_t X_MAP_MAX = 64;
constexpr int8_t Y_MAP_MIN = 30;
constexpr int8_t Y_MAP_MAX = -30;
// 디스플레이 업데이트 딜레이
constexpr uint8_t DISPLAY_UPDATE_DELAY = 50;
// 빠른 통신 객체 선언 방법
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); // Fast I2C / TWI
// 핀 설정
const uint8_t joystick_x_pin = A1; // 조이스틱 X축 핀 (아날로그)
const uint8_t joystick_y_pin = A2; // 조이스틱 Y축 핀 (아날로그)
// 그래프 값 변수
int x_value = 0;
int y_value = 0;
// 함수 선언
void init_oled();
void update_oled_display();
void draw_axes_and_labels();
void draw_x_bar_graph(int value);
void draw_y_bar_graph(int value);
void setup()
{
// 시리얼 통신 초기화
Serial.begin(9600);
Serial.println("OLED -x/+x & -y/+y Bar Graph Example");
// OLED 초기화
init_oled();
}
void loop()
{
// 조이스틱 값 읽기 (0-1023)
uint16_t joystick_x_raw = analogRead(joystick_x_pin);
uint16_t joystick_y_raw = analogRead(joystick_y_pin);
// 값 변환: 0-1023 범위를 -64에서 +64 (oled 픽셀 범위)
// 512가 중간값(0)이 되도록 함
x_value = map(joystick_x_raw, JOY_MIN, JOY_MAX, X_MAP_MIN, X_MAP_MAX); // X축 원래 방향으로 복원 (왼쪽이 -x, 오른쪽이 +x)
y_value = map(joystick_y_raw, JOY_MIN, JOY_MAX, Y_MAP_MIN, Y_MAP_MAX); // Y축은 반전 유지 (위가 +y, 아래가 -y)
// 시리얼로 값 출력 (디버깅용) , 필요할경우 활성화
// Serial.print("X: ");
// Serial.print(joystick_x_raw);
// Serial.print(" -> ");
// Serial.print(x_value);
// Serial.print(", Y: ");
// Serial.print(joystick_y_raw);
// Serial.print(" -> ");
// Serial.println(y_value);
// 화면 업데이트
update_oled_display();
// 화면 갱신 대기 시간
delay(DISPLAY_UPDATE_DELAY);
}
// OLED 초기화 함수
void init_oled()
{
if (u8g.begin() == 0)
{
Serial.println("OLED 디스플레이 초기화 실패");
while (1)
; // 초기화 실패 시 무한 루프
}
u8g.setFont(u8g_font_6x10);
u8g.setColorIndex(1); // 흑백 OLED이므로 1은 흰색
}
// OLED 디스플레이 업데이트 함수
void update_oled_display()
{
u8g.firstPage();
do
{
draw_axes_and_labels();
draw_x_bar_graph(x_value);
draw_y_bar_graph(y_value);
} while (u8g.nextPage());
}
// 중앙 축과 라벨 그리기
void draw_axes_and_labels()
{
// 중앙 수직선과 수평선 그리기 (십자 모양)
u8g.drawVLine(CENTER_X, 0, SCREEN_HEIGHT); // 중앙 수직선
u8g.drawHLine(0, CENTER_Y, SCREEN_WIDTH); // 중앙 수평선
// -x, +x, -y, +y 라벨 표시 (X축 라벨은 원래대로, Y축 라벨은 반전 유지)
u8g.setFont(u8g_font_6x10);
u8g.drawStr(LABEL_MINUS_X_X, LABEL_MINUS_X_Y, "-x"); // X축 원래 위치로 복원
u8g.drawStr(LABEL_PLUS_X_X, LABEL_PLUS_X_Y, "+x"); // X축 원래 위치로 복원
u8g.drawStr(LABEL_MINUS_Y_X, LABEL_MINUS_Y_Y, "-y"); // Y축 위치 유지
u8g.drawStr(LABEL_PLUS_Y_X, LABEL_PLUS_Y_Y, "+y"); // Y축 위치 유지
}
// X축 막대 그래프 그리기 (가로 방향)
void draw_x_bar_graph(int value)
{
if (value < 0)
{
// 음수 값인 경우 왼쪽에 그리기 (원래 방향으로 복원)
int bar_length = abs(value);
u8g.drawBox(CENTER_X - bar_length, X_BAR_Y_POS - X_BAR_HEIGHT / 2,
bar_length, X_BAR_HEIGHT);
}
else if (value > 0)
{
// 양수 값인 경우 오른쪽에 그리기 (원래 방향으로 복원)
u8g.drawBox(CENTER_X, X_BAR_Y_POS - X_BAR_HEIGHT / 2,
value, X_BAR_HEIGHT);
}
// X 값 표시
char value_str[10];
sprintf(value_str, "X:%d", value);
u8g.drawStr(X_VALUE_X, X_VALUE_Y, value_str);
}
// Y축 막대 그래프 그리기 (세로 방향) - 변경 없음, 반전 유지
void draw_y_bar_graph(int value)
{
if (value < 0)
{
// 음수 값인 경우 아래쪽에 그리기 (반전됨)
int bar_length = abs(value);
u8g.drawBox(Y_BAR_X_POS - Y_BAR_WIDTH / 2, CENTER_Y,
Y_BAR_WIDTH, bar_length);
}
else if (value > 0)
{
// 양수 값인 경우 위쪽에 그리기 (반전됨)
u8g.drawBox(Y_BAR_X_POS - Y_BAR_WIDTH / 2, CENTER_Y - value,
Y_BAR_WIDTH, value);
}
// Y 값 표시
char value_str[10];
sprintf(value_str, "Y:%d", value);
u8g.drawStr(Y_VALUE_X, Y_VALUE_Y, value_str);
}