아두이노 나노로 만드는 휴대용 가이거 계수기
부품 및 소모품
| 아두이노 나노 R3 |
1개 |
| 기성 가이거 계수기 키트 (조립 완료품) |
1개 |
| 0.96인치 컬러 OLED 디스플레이 96x64 |
1개 |
| 마이크로 USB 충전 보드 (18650 배터리용) |
1개 |
| 3.7V 4000mAh 보호 회로 내장 18650 리튬이온 배터리 |
1개 |
| 트랜지스터 BC547 |
1개 |
| 부저 12mm |
1개 |
| 저항 1k옴 |
1개 |
필요한 도구 및 장비
앱 및 온라인 서비스
- 오토데스크 퓨전 360 (케이스 설계)
- 아두이노 IDE
- CURA (슬라이서)
- ClickCharts (순서도)
- EasyEDA (회로도)
- Fritzing (브레드보드 레이아웃)
프로젝트 소개
이 프로젝트는 뱅굿에서 기성 가이거 계수기 키트를 구매하면서 시작되었습니다.
이 키트를 3D 프린팅한 하우징에 넣어 완전히 작동하는 휴대용 가이거 계수기 세트를 만드는 것이 아이디어였습니다. 최종 결과물은 아래와 같습니다.
1단계: 시스템 설계
휴대용 가이거 계수기 설계도는 아래 다이어그램과 같습니다.
0.96인치 컬러 OLED 디스플레이를 장착해 측정된 CPM(분당 계수, 이온화 사건 검출률)과 계산된 선량 당량(µSv/hr)을 표시합니다. µSv/hr 변환은 사용된 가이거-뮐러(GM) 관 타입에 대한 문헌 값인 151 계수를 사용했습니다(위키피디아: Counts per minute 참조).
표시되는 CPM은 초당 계수(CPS)를 측정해 최근 10초간 측정값을 배열에 저장하고, 이 10초간 총 계수에 6을 곱해 산출합니다. 직전 1초 계수로는 OLED에 막대 그래프로 순간적 측정치를 표시해 고선량이나 급격한 변화 시 유용합니다.
전원은 18650 리튬이온 배터리(마이크로 USB로 충전). 아두이노 나노 USB 포트도 소프트웨어 업데이트용으로 접근 가능. 기성 키트의 부저 소리가 작아 외부 부저 추가.
모든 전자 부품은 3D 프린팅 하우징 내부에 장착. OLED 디스플레이는 가이거 계수기 위쪽 별도 박스에 장착. 완전 조립품 사진 참조.
2단계: 가이거 계수기 조립
사용 재료:
- 아두이노 나노: 공식 스토어
- 가이거 계수기 키트: 뱅굿
- 0.96인치 컬러 OLED 96x64: 뱅굿
- 마이크로 USB 충전 보드: 뱅굿
- 18650 배터리: 뱅굿
- 트랜지스터 BC547, 부저 12mm, 저항 1k옴 각 1개
전자 회로 설계
가이거 계수기 키트 회로도와 전체 회로도, 브레드보드 테스트 설정 사진 참조. 5V는 충전 보드에 장착된 18650 배터리에서 공급, OLED용 3.3V도 이 보드에서 가져옴.
3단계: 소프트웨어 설계
소프트웨어 전체 흐름 순서도와 OLED 표시 화면 구성 참조. 전체 아두이노 스케치는 아래와 같습니다(약 150줄, 풍부한 주석 포함).
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#include <SPI.h>
// OLED 연결 핀
#define sclk 13 // SCL (파랑)
#define mosi 11 // SDA (하양)
#define cs 10 // CS (회색)
#define rst 9 // RES (초록)
#define dc 8 // DC (노랑)
#define LOGtime 1000 // 로깅 간격 1초(ms)
#define Minute 60000 // CPM 계산용 1분 주기
#define show endWrite
#define clear() fillScreen(0)
// 색상 정의
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, rst);
int Counts = 0; // LOGtime 내 GM 관 이벤트 수
unsigned long previousMillis = 0; // 이전 시간 저장
int AVGCPM = 0; // 고정 이동 윈도우 기간 평균 계수
int TenSecCPM = 0;
int units = 0, tens = 0, hundreds = 0, thousands = 0;
float Sievert = 0;
int COUNTS[10]; // 연속 10초간 1초당 측정된 임펄스 저장 배열
int t = 0;
void setup() {
Serial.begin(115200);
display.begin();
display.fillScreen(BLACK);
float Battery = analogRead(A3); // 배터리 전압 읽기 (주황 선)
float BattPerc = 100 * (Battery/770);
// 시리얼 디버그 출력 생략
display.setCursor(4,4);
display.setTextSize(2);
display.setTextColor(MAGENTA);
display.println("Battery");
display.setCursor(4,24);
display.print(int(BattPerc)); display.print("."); display.print(int((10*BattPerc)-(10*int(BattPerc)))); display.print(" %");
delay(3000);
display.fillScreen(BLACK);
for(int x = 0; x < 10 ; x++) COUNTS[x] = 0; // 배열 초기화
attachInterrupt(0, IMPULSE, FALLING); // D2/INT0 외부 인터럽트 설정 (녹색 선)
display.drawRect(0,0,96,64,WHITE);
display.setCursor(4,4);
display.setTextColor(RED);
display.setTextSize(2);
display.print("CPM");
display.setCursor(50,4);
display.setTextSize(1);
display.print("10 sec");
display.setCursor(50,12);
display.print("window");
display.setCursor(4,38);
display.setTextSize(1);
display.setTextColor(GREEN);
display.print("uSv/hr");
display.drawRect(0,48, 96, 16, YELLOW);
}
void loop() {
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > LOGtime) {
previousMillis = currentMillis;
COUNTS[t] = Counts;
for (int y = 0; y < 10 ; y++) TenSecCPM += COUNTS[y]; // 최근 10초 합산
AVGCPM = 6 * TenSecCPM; // CPM 환산
TenSecCPM = 0;
t++; if (t > 9) t = 0;
// 디버그 출력 생략
display.fillRect(4,20,90,17,BLACK); // CPM 값 영역 지우기
display.setCursor(4,20);
display.setTextColor(RED);
display.setTextSize(2);
display.println(AVGCPM);
display.fillRect(45,38,50,10,BLACK); // µSv/hr 영역 지우기
display.setCursor(45,38);
display.setTextColor(GREEN);
display.setTextSize(1);
Sievert = (AVGCPM / 151.0); // µSv/hr 계산
units = int(Sievert);
tens = int((10*Sievert) - (10*units));
hundreds = int((100*Sievert) - (100*units) - (10*tens));
thousands = int((1000*Sievert) - (1000*units) - (100*tens) - (10*hundreds));
display.print(units); display.print("."); display.print(tens); display.print(hundreds); display.println(thousands);
display.fillRect(1,49,94,14,BLACK); // CPM 표시 바 영역 지우기
display.fillRect(1,49,Counts,14,RED); // 직전 1초 계수(CPS) 막대 그래프
Counts = 0;
}
}
// GM 관 임펄스 인터럽트 루틴 (INT 신호가 LOW 펄스로 들어옴)
void IMPULSE() { Counts++; }
가장 중요한 부분은 GM 관에서 임펄스가 감지되어 가이거 계수기의 INT 출력이 순간적으로 LOW가 될 때 호출되는 인터럽트 함수입니다. INT 신호는 아두이노 나노의 D2 핀(외부 인터럽트 INT0)에 연결됩니다.
attachInterrupt(0, IMPULSE, FALLING);
INT 신호가 들어오면 IMPULSE() 루틴이 실행돼 Counts++ 합니다.
1000ms 경과 후: Counts를 0으로 리셋, COUNTS[] 배열에 최근 1초 계수 저장, 배열 전체 합산×6으로 CPM 계산해 표시. CPM/151로 µSv/hr 산출 표시. 컬러 OLED에 직전 1초 계수(Counts) 기반 빨간 막대 그래프로 CPS 표시.
코드
Geiger_Counter.ino (아두이노) - 위 전체 스케치 참조
3D 프린팅 파일
- 0.96인치 OLED 디스플레이용 하우징
- 퓨전 360으로 설계한 상/하 분리형 메인 하우징 (STL/OBJ)
회로도
전자 부품 연결 설정도 참조