직접 만든 A4 크기 7세그먼트 디스플레이로 아두이노 빙고 머신 만들기
부품 및 소모품
| 부품 |
수량 |
비고 |
| 펙 보드 (Peg Board) |
3 |
7세그먼트 디스플레이 2개용, 스코어보드 1개용 |
| 플라스틱 도마 (회색) |
1 |
스코어보드용 |
| 디지털 LED 스트립 5m WS2811 (5050 LED) |
1 |
다수 벤더 판매 |
| DC5V WS2812B 300LED (60LED/m, 5050 RGB) |
1 |
다수 벤더 판매 |
| 오디오 케이블 |
1 |
스코어보드와 7세그먼트 디스플레이 간 연결용 |
| 투명 플라스틱 도마 |
2 |
7세그먼트 디스플레이 전면 패널용 |
| 유연 플라스틱 도마 (회색) |
1 |
스코어보드 커버용 |
| 아두이노 우노 (Arduino UNO) |
1 |
|
| 저항 220옴 |
2 |
|
| 커패시터 100µF |
2 |
|
| 대형 푸시버튼 스위치 (모멘터리) |
1 |
|
| 소형 푸시버튼 (일반) |
1 |
|
| 리니어 레귤레이터 (7805) |
1 |
|
필요 도구
프로젝트 소개
ACTION 매장에서 저렴한 디지털 LED 스트립(12V 어댑터 및 IR 리모컨 컨트롤러 포함)을 발견하고 아두이노 우노로 제어해보기로 했습니다. 이 스트립은 3개 5050 LED와 WS2811 드라이버 1개로 구성된 세그먼트 타입이었습니다.
이를 이용해 A4 크기의 7세그먼트 디스플레이를 만들기로 했습니다. 7개 세그먼트 + 소수점(DP) = 8개 세그먼트가 필요하며, 아두이노 출력 핀 1개와 12V 어댑터로 제어 가능합니다.
단일 7세그먼트 디스플레이로는 뭘 할 수 있을까 고민하다, 2개를 만들어 빙고 머신을 만들기로 했습니다. 버튼을 누르면 1~75 사이의 랜덤 숫자가 두 디스플레이에 표시됩니다.
1단계: 7세그먼트 디스플레이 제작
레이저 커터나 3D 프린터를 쓰면 더 간단하지만, 직접 만드는 재미로 기존 재료로 제작했습니다. 액션 매장의 펙 보드를 하우징으로 사용하고, 이케아의 투명 도마로 전면 패널을 만들었습니다. 템플릿으로 검은 프라이머 층을 잘라 세그먼트 모양을 냈습니다.
LED 스트립 16개 세그먼트(각 3 LED)를 납땜으로 연결(+12V, GND, DO, DI)하고, 저렴한 오디오 케이블(3선: 12V, GND, Data in)로 아두이노와 연결(220옴 저항 경유)했습니다.
2단계: 스코어보드 제작
생성된 랜덤 숫자를 기록하고, 새 숫자 생성을 위한 버튼, 빙고 완료를 위한 '빙고 버튼'을 갖춘 스코어보드를 만들었습니다. 같은 펙 보드 하우징에 회색 도마로 커버를 만들고, 내부에는 WS2812B LED 스트립(75개 숫자 표시용 5개 섹션 + 'BINGO' 백라이트용 3x8 섹션)을 배치했습니다.
3단계: 소프트웨어 제작
초기 실험은 브레드보드에서 진행했습니다. 큰 모멘터리 푸시버튼을 GND와 디지털 핀 2에 연결해 새 숫자 생성을 트리거합니다. 짧은 '라이트 쇼 1' 후 두 7세그먼트 디스플레이에 숫자가 나타납니다.
7세그먼트 숫자 매핑 테이블과 플로우차트(ClickCharts 사용)로 코드 구조를 잡았습니다. 생성된 숫자는 SCORE[76] 배열(0 또는 1)에 저장되며, 중복 시 자동으로 다시 생성합니다. 스코어보드에는 생성된 숫자가 점등되고, 7세그먼트에도 동시 표시됩니다.
'빙고 버튼'을 누르면 '빙고 라이트 쇼' 후 라운드 종료, SCORE 배열 초기화 후 새 라운드 시작. 전원 토글로도 리셋 가능.
4단계: 전자 회로
12V 2A 충전기로 전체 전원 공급. 아두이노 전원 잭을 수정해 파워 스위치 추가. 7805 레귤레이터로 5V 생성해 스코어보드용 99개 LED(WS2812B) 구동(히트싱크 권장). 프리칭 다이어그램 참조.
두 7세그먼트 디스플레이(12V 구동)와 스코어보드 LED(5V 구동) 모두 아두이노 핀 6 하나로 제어됩니다.
코드
/* Bingo Machine with Arduino Uno and 2 A4 sized 7-Segment Displays
Developed by Pierre Pennings (November 2018) - GPL3+ license */
#include <Adafruit_NeoPixel.h>
const int NewnumberButton = 2;
const int BingoButton = 4;
const int LedPin = 6;
int Newnumber = 1, Bingo = 1;
int SCORE[76];
int count = 0;
long randNumber;
int NUMBER = 0, NW_NUMBER = 0, TENSNUMBER = 0, UNITNUMBER = 0;
#define NUM_LEDS 99
// 0-15: 두 7세그먼트 디스플레이 (각 8세그먼트, 7과 15는 DP)
// 16-99: 스코어보드 (24개 BINGO 백라이트 + 75개 숫자)
#define BRIGHTNESS 250
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LedPin, NEO_GRB + NEO_KHZ800);
// 숫자-세그먼트 매핑 배열 [세그먼트][숫자]
byte SEGMENTarray[8][10] = {
{1,0,1,1,0,1,1,1,1,1}, // 세그먼트 0/8
{1,1,1,1,1,0,0,1,1,1}, // 세그먼트 1/9
{1,1,0,1,1,1,1,1,1,1}, // 세그먼트 2/10
{1,0,1,1,0,1,1,0,1,1}, // 세그먼트 3/11
{1,0,1,0,0,0,1,0,1,0}, // 세그먼트 4/12
{1,0,0,0,1,1,1,0,1,1}, // 세그먼트 5/13
{0,0,1,1,1,1,1,0,1,1}, // 세그먼트 6/14
{0,0,0,0,0,0,0,0,0,0} // 세그먼트 7/15 (DP)
};
byte color_scheme[] = { 50,100,200,100,150,250,150,200,50,200,250,100,250,50,150,0,100,200,50,150,250,100,200,0,150,250,50,200,0,100,250,50,200,0,100,250,50,150,0,250,0,0 };
void setup() {
Serial.begin(9600);
pinMode(LedPin, OUTPUT);
pinMode(NewnumberButton, INPUT_PULLUP);
pinMode(BingoButton, INPUT_PULLUP);
strip.setBrightness(BRIGHTNESS);
strip.begin();
strip.show(); // 모두 끄기
// 시작 시 스코어보드에 파란색 'BINGO' 표시
for (int t = 16; t < 24; t++) {
strip.setPixelColor(t, 0, 0, 250);
strip.show();
}
for (count = 0; count < 76; count++) SCORE[count] = 0;
}
void loop() {
Newnumber = digitalRead(NewnumberButton);
if (Newnumber == LOW) {
randomSeed(millis());
do {
GENERATENEWNUMBER(75);
} while (NW_NUMBER == SCORE[NW_NUMBER] * NW_NUMBER);
SCORE[NW_NUMBER] = 1;
NUMBER = NW_NUMBER;
TENSNUMBER = NUMBER / 10;
UNITNUMBER = NW_NUMBER - (10 * TENSNUMBER);
CLEARDISPLAY();
LIGHTSHOW1(4, 100);
CLEARDISPLAY();
DISPLAYNUMBER(TENSNUMBER, UNITNUMBER);
DISPLAYSCORE();
} else {
Bingo = digitalRead(BingoButton);
if (Bingo == LOW) delay(3000); // 디바운스
if (Bingo == LOW) {
BINGOLIGHTSHOW();
for (count = 0; count < 76; count++) SCORE[count] = 0;
}
}
}
// 라이트 쇼 1: DP 깜빡임 + 레인보우 색상
void LIGHTSHOW1(int duration, uint8_t wait) {
for (int t = 16; t < 24; t++) { strip.setPixelColor(t, 0, 0, 0); strip.show(); }
for (int k = 0; k < duration; k++) {
strip.setPixelColor(7, 200, 200, 200); strip.show(); delay(wait);
strip.setPixelColor(7, 0, 0, 0);
strip.setPixelColor(15, 200, 200, 200); strip.show(); delay(wait);
strip.setPixelColor(15, 0, 0, 0);
}
int redVal, blueVal, greenVal;
for (int k = 1; k < 45; k += 3) {
redVal = color_scheme[k];
blueVal = color_scheme[k+1];
greenVal = color_scheme[k+2];
for (int p = 0; p < 7; p++) {
int i = p; int j = 8 + p;
strip.setPixelColor(i, strip.Color(redVal, blueVal, greenVal));
strip.setPixelColor(j, strip.Color(greenVal, redVal, blueVal));
strip.show(); delay(30);
}
}
for (int q = 0; q < 7; q++) {
strip.setPixelColor(q, 0, 0, 0);
strip.setPixelColor(8+q, 0, 0, 0);
strip.show();
}
}
// 빙고 라이트 쇼
void BINGOLIGHTSHOW() {
for (int l = 0; l < 10; l++) {
for (int k = 24; k < 39; k++) {
strip.setPixelColor(k, 200,200,200);
strip.setPixelColor(k+15, 200,200,200);
strip.setPixelColor(k+30, 200,200,200);
strip.setPixelColor(k+45, 200,200,200);
strip.setPixelColor(k+60, 200,200,200);
strip.show(); delay(5);
strip.setPixelColor(k, 0,0,0);
strip.setPixelColor(k+15, 0,0,0);
strip.setPixelColor(k+30, 0,0,0);
strip.setPixelColor(k+45, 0,0,0);
strip.setPixelColor(k+60, 0,0,0);
strip.show();
}
for (int t = 0; t < 24; t++) { strip.setPixelColor(t, 0,250,250); strip.show(); }
delay(50);
for (int t = 0; t < 24; t++) { strip.setPixelColor(t, 0,0,0); strip.show(); }
}
}
// 새 숫자 생성
void GENERATENEWNUMBER(int range) {
randNumber = random(1, range + 1);
NW_NUMBER = int(randNumber);
}
// 7세그먼트 디스플레이에 숫자 표시 (빨간색)
void DISPLAYNUMBER(int TENS, int UNIT) {
for (int t = 16; t < 24; t++) { strip.setPixelColor(t, 200, 0, 0); strip.show(); }
for (int s = 0; s < 8; s++) {
int t = SEGMENTarray[s][TENS];
int u = SEGMENTarray[s][UNIT];
int i = s; int j = 8 + s;
strip.setPixelColor(i, u*250, 0, 0);
if (TENS != 0) strip.setPixelColor(j, t*250, 0, 0);
else strip.setPixelColor(j, 0, 0, 0);
strip.show();
}
}
// 스코어보드에 숫자 표시 (기존: 녹색, 새 숫자: 빨간색)
void DISPLAYSCORE() {
for (int t = 16; t < 24; t++) { strip.setPixelColor(t, 200, 0, 0); strip.show(); }
for (int s = 24; s < 100; s++) {
int u = SCORE[s-23];
strip.setPixelColor(s, 0, u*200, 0);
strip.show();
}
strip.setPixelColor(23+NUMBER, 200, 0, 0); // 새 숫자 빨간색
strip.show();
}
// 7세그먼트 디스플레이 지우기
void CLEARDISPLAY() {
for (int q = 0; q < 7; q++) {
strip.setPixelColor(q, 0, 0, 0);
strip.setPixelColor(8+q, 0, 0, 0);
strip.show();
}
for (int t = 16; t < 24; t++) { strip.setPixelColor(t, 0, 0, 0); strip.show(); }
}
커스텀 파트 및 인클로저
회로도
- Fritzing 파일:
bingo_sketch_66WRdQITE2.fzz