|
|
아두이노와 OLED를 연결한 상태에서 아래 코드를 복사해 업로드합니다.
시리얼 모니터를 열고 보드레이트를 9600으로 맞춥니다.
화면에 I2C device found at address 0xXX라고 뜨는 주소를 확인합니다.
2. I2C 스캐너 소스 코드(아두이노에 아래의 코드를 붙여넣어 실행하면 어드레스가 Serial Monitor로 확 할 수 있습니다.
// ============================================
// I2C 스캐너 (I2C Scanner)
// 동작: 연결된 모든 I2C 장치의 주소를 찾아
// 시리얼 모니터에 출력합니다.
// 사용법: 업로드 후 시리얼 모니터 열기 (9600bps)
// ============================================
#include <Wire.h> // I2C 통신 라이브러리 (아두이노 기본 내장)
void setup() {
Wire.begin(); // I2C 통신 시작 (SDA=A4, SCL=A5 자동 설정)
Serial.begin(9600); // 시리얼 통신 시작 (모니터 속도 9600bps)
while (!Serial); // 시리얼 포트 준비될 때까지 대기
// (Leonardo, Micro 보드 필요. Uno는 없어도 무방)
Serial.println("\nI2C Scanner"); // 시작 메시지 출력
}
void loop() {
byte error; // I2C 통신 오류 코드 저장 변수
byte address; // 검사할 I2C 주소 (1~126)
int nDevices = 0; // 발견된 장치 수 카운터
Serial.println("Scanning..."); // 스캔 시작 메시지
// ── I2C 주소 전체 범위 순서대로 검사 ──────────────
// I2C 주소는 7비트 → 유효 범위: 0x01 ~ 0x7E (1 ~ 126)
// 0x00은 브로드캐스트 주소라 제외
for (address = 1; address < 127; address++) {
// 해당 주소로 전송 시작 (데이터 없이 주소만 보냄)
Wire.beginTransmission(address);
// 전송 종료 후 오류 코드 확인
// 반환값: 0 = 성공(장치 있음), 2 = 주소 없음, 그 외 = 오류
error = Wire.endTransmission();
if (error == 0) {
// ── 장치 발견! 주소 출력 ────────────────────
Serial.print("I2C device found at address 0x");
// 주소가 16(0x10) 미만이면 앞에 "0" 붙여서
// 0x3C → "0x3C" / 0x09 → "0x09" 형식으로 출력
if (address < 16) Serial.print("0");
Serial.print(address, HEX); // 16진수로 주소 출력
Serial.println(" !");
nDevices++; // 발견 장치 수 증가
}
}
// ── 스캔 결과 출력 ────────────────────────────────
if (nDevices == 0)
Serial.println("No I2C devices found\n"); // 장치 없음
else
Serial.println("done\n"); // 스캔 완료
delay(5000); // 5초 후 다시 스캔 (장치 탈착 감지용)
}
출력 예시 (시리얼 모니터)
I2C Scanner
Scanning...
I2C device found at address 0x3C !
done
Scanning...
I2C device found at address 0x3C !
done
코드 실행 후 시리얼 모니터 화면, 어드레스를 리턴해 보여줍니다.
4.3 브레드보드 배선
브레드보드를 활용한 아두이노 UNO R3와 SSD1306 OLED 배선) [ Vcc, GND, SCL-A5(초록선), SDA-A4(노랑선) 배선] [도면작성 툴: Wokwi]
5장. 기본 예제 — 이름 표시 & 카운터 프로그램
접기/펴기
아두이노 I2C OLED 기본 동작, 텍스트, 카운터 동작
5.1 예제 개요
이 예제는 OLED 화면에 이름(타이틀)을 표시하고, 1초마다 카운터가 증가하는 기본 프로그램입니다. OLED 초기화, 텍스트 크기 설정, 구분선 그리기, 부분 화면 갱신 기법을 모두 담고 있습니다.
5.2 동작 설명
• 전원 켜면 타이틀(MIN T)과 부제목이 표시됩니다.
• 하단에 가로 구분선이 그려집니다.
• 1초마다 Count 값이 1씩 증가합니다.
• fillRect()로 카운터 영역만 지우고 다시 써서 깜빡임을 최소화합니다.
5.3 소스 코드 (주석 포함)
// ============================================
// 민선생의 전자공방 - OLED 기본 예제
// 파일명: ex_oled_basic.ino
// 동작: 타이틀 표시 + 1초 카운터
// 보드: Arduino Uno R3
// OLED: SSD1306 128x64 I2C (주소 0x3C)
// ============================================
// ── 라이브러리 포함 ───────────────────────────
#include <Wire.h> // I2C 통신 라이브러리
#include <Adafruit_GFX.h> // GFX 그래픽 기반 라이브러리
#include <Adafruit_SSD1306.h> // SSD1306 OLED 드라이버
// ── 상수 정의 ─────────────────────────────────
#define SCREEN_W 128 // OLED 가로 픽셀 수
#define SCREEN_H 64 // OLED 세로 픽셀 수
#define OLED_ADDR 0x3C // I2C 주소 (0x3D인 모듈도 있음)
#define OLED_RESET -1 // 리셋 핀 없으면 -1
// ── OLED 객체 생성 ────────────────────────────
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, OLED_RESET);
// ── 카운터 변수 (전역) ────────────────────────
int cnt = 0;
void setup() {
Serial.begin(9600); // 시리얼 모니터 (디버그용)
// OLED 초기화 - 실패 시 무한 대기
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("SSD1306 init failed");
while (1); // 초기화 실패 시 정지
}
display.clearDisplay(); // 화면 전체 지우기
// ── 큰 글자 타이틀 출력 ───────────────────
// TextSize(2) = 12px 글자, 10글자까지 표시 가능
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0); // 좌상단 시작
display.println("MIN T"); // 타이틀 출력
// ── 작은 글자 부제목 ─────────────────────
display.setTextSize(1); // TextSize(1) = 6px
display.setCursor(0, 20); // y=20 위치에 커서
display.println("Min teacher's shop");
display.println("SSD1306 OLED Test");
// ── 수평 구분선 그리기 ───────────────────
// drawLine(x0, y0, x1, y1, color)
display.drawLine(0, 42, 127, 42, SSD1306_WHITE);
// ── 카운터 초기값 표시 ───────────────────
display.setCursor(0, 50);
display.print("Count: 0");
display.display(); // 버퍼 내용을 실제 화면에 출력
}
void loop() {
cnt++; // 카운터 1 증가
// ── 카운터 영역만 지우기 ─────────────────
// fillRect(x, y, width, height, color)
// y=50 에서 높이 14px 영역을 검은색으로 채움
display.fillRect(0, 50, 128, 14, SSD1306_BLACK);
// ── 새 카운터 값 출력 ────────────────────
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 50);
display.print("Count: ");
display.print(cnt); // 정수값 출력
display.display(); // 화면 갱신
delay(1000); // 1초 대기
}
6장. 심화 과제 — 바운싱 볼 애니메이션
브레드보드를 활용한 아두이노 UNO R3와 SSD1306 OLED 배선) [ Vcc, GND, SCL-A5(초록선), SDA-A4(노랑선) 배선] [도면작성 툴: Wokwi]
접기/펴기
아두이노 OLED 공바운스 동작영상
6.1 예제 개요
이 예제는 화면 안에서 공이 벽에 부딪힐 때마다 방향을 바꾸며 튕기는 애니메이션입니다. float 타입의 속도 변수(vx, vy)와 충돌 감지 로직을 이해하면 다양한 그래픽 애니메이션의 기초가 됩니다.
6.2 동작 원리
• 공의 위치(bx, by)에 매 루프마다 속도(vx, vy)를 더해 이동합니다.
• 공이 화면 테두리(벽)에 닿으면 해당 방향의 속도 부호를 반전시킵니다.
• clearDisplay() → 그리기 → display() 를 반복해 애니메이션을 구현합니다.
• delay(20) = 약 50fps 속도로 부드럽게 움직입니다.
| 💡 vx=2.5, vy=1.8 의 비율이 중요합니다! 두값이 같으면 공이 대각선만 반복합니다. 서로 소수인 비율을 사용하면 공이 화면 전체를 고루 누비는 예쁜 궤적을 그립니다. |
6.3 소스 코드 (주석 포함)
// ============================================
// 민선생의 전자공방 - OLED 바운싱 볼
// 파일명: Oled_bounce_ball.ino
// 동작: 공이 화면 벽에서 튕기는 애니메이션
// 보드: Arduino Uno R3
// OLED: SSD1306 128x64 I2C (주소 0x3C)
// ============================================
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ── 상수 정의 ─────────────────────────────────
#define SCREEN_W 128 // 화면 가로 픽셀
#define SCREEN_H 64 // 화면 세로 픽셀
#define OLED_ADDR 0x3C // I2C 주소
#define OLED_RESET -1 // 리셋 핀 없음
#define BALL_R 5 // 공 반지름 (픽셀)
// 크게 하면 공이 커짐
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, OLED_RESET);
// ── 공 상태 변수 ──────────────────────────────
float bx = 20, by = 20; // 공의 현재 X, Y 좌표
// float: 소수점 이하로 부드럽게 이동
float vx = 2.5, vy = 1.8; // X, Y 방향 속도
// 서로 소 비율 → 다양한 궤적
int bounces = 0; // 벽 충돌 횟수 카운터
void setup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
while (1); // 초기화 실패 시 정지
}
display.clearDisplay();
}
void loop() {
// ── 공 위치 업데이트 ─────────────────────
bx += vx; // X 위치 += X 속도
by += vy; // Y 위치 += Y 속도
// ── X축 벽 충돌 감지 ─────────────────────
if (bx - BALL_R <= 0) { // 왼쪽 벽 충돌
bx = BALL_R; // 벽 안쪽으로 위치 보정
vx = abs(vx); // X 속도를 양수로 (오른쪽으로)
bounces++;
}
if (bx + BALL_R >= SCREEN_W - 1) { // 오른쪽 벽 충돌
bx = SCREEN_W - 1 - BALL_R;
vx = -abs(vx); // X 속도를 음수로 (왼쪽으로)
bounces++;
}
// ── Y축 벽 충돌 감지 ─────────────────────
if (by - BALL_R <= 0) { // 상단 벽 충돌
by = BALL_R;
vy = abs(vy); // Y 속도를 양수로 (아래쪽으로)
bounces++;
}
if (by + BALL_R >= SCREEN_H - 1) { // 하단 벽 충돌
by = SCREEN_H - 1 - BALL_R;
vy = -abs(vy); // Y 속도를 음수로 (위쪽으로)
bounces++;
}
// ── 화면 그리기 ──────────────────────────
display.clearDisplay(); // 이전 프레임 지우기
// 화면 테두리 그리기
display.drawRect(0, 0, SCREEN_W, SCREEN_H, SSD1306_WHITE);
// 공 그리기 (속이 찬 원)
// float → int 캐스팅으로 픽셀 단위 변환
display.fillCircle((int)bx, (int)by, BALL_R, SSD1306_WHITE);
// 좌표 및 충돌 횟수 표시
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(4, 2);
display.print("x:"); display.print((int)bx);
display.print(" y:"); display.print((int)by);
display.setCursor(80, 2);
display.print("B:"); display.print(bounces);
display.display(); // 버퍼 → 화면 출력
delay(20); // 20ms 대기 = 약 50fps
}
6.4 파라미터 변경으로 다양한 효과
| 변수/상수 | 변경 예시 | 효과 |
| BALL_R | 3 → 10 | 공 크기 변화 |
| vx, vy | 1.0, 1.0 → 4.0, 3.5 | 공 이동 속도 빠르게 |
| delay(20) | 20 → 50 | 프레임 속도 느리게 |
| fillCircle | drawCircle으로 변경 | 속이 빈 공(테두리만) |
다음 과제 예고
다음 포스팅에서는 서보모터(Servo Motor)를 추가하여 OLED 화면에 서보의 현재 각도를 실시간으로 표시하는 과제를 진행합니다.
• 서보모터 PWM 제어 원리
• I2C OLED + 서보모터 동시 제어
• OLED에 각도 게이지 바 그래픽 표시