고성능 아두이노 기상 관측용 데이터 로거 제작 가이드
부품 및 소모품
부품 수량
Maxim Integrated DS18B20 프로그래머블 해상도 1-Wire 디지털 온도계 2
DHT22 온도/습도 센서 1
SparkFun 3축 가속도계 브레이크아웃 - ADXL335 1
수컷 헤더 36핀 1열 롱 (0.1") 2
Arduino Mega 2560 1
5050 SMD LED 1
저항 4.75kΩ 2
저항 100Ω 3
저항 10kΩ 1
마이크로 SD 카드 1
마이크로 SD 카드 모듈 1
필요 도구 및 장비
앱 및 온라인 서비스
프로젝트 개요
여러분, 아두이노를 우주로 보낼 수 있다는 걸 아시나요? 거의요. 기상 관측용 풍선을 이용하면 아두이노, 카메라, 마늘빵 등 원하는 것을 성층권, 즉 '우주의 가장자리'까지 보낼 수 있습니다. 대기의 99%가 없는 고도이므로 사실상 우주 환경과 비슷합니다. 이 가이드는 꽤 길지만, 경로 1까지는 반드시 모두 읽어보시기 바랍니다.
실력과 목적에 따라 세 가지 경로 중 하나를 선택하세요. 모든 경로는 성층권의 강풍을 견디기 위해 어느 정도 납땜 기술이 필요합니다. 어느 경로가 더 나은 건 아니며, 단지 관심사에 따라 다를 뿐입니다.
경로 1: 프로토 보드를 이용한 간단한 데이터 로거
프로토 보드, 각종 센서, 최소한의 납땜으로 제작합니다. 센서와 보드, 기능을 자유롭게 선택할 수 있어 가장 유연합니다.
경로 2: 사전 설계된 PCB 실드 사용
제작자가 설계하고 테스트한 PCB 실드를 사용합니다. 지정된 센서와 아두이노 보드를 써야 하지만, 깔끔한 외관에 납땜과 코딩 걱정이 적고 PCB 입문에 좋습니다. 거버 파일, 내보내기 설정, 코드를 제공하며, 5장에 $2 정도로 저렴합니다.
경로 3: 직접 PCB 설계
가장 어렵고 많은 기술이 필요합니다. PCB/실드 설계, 코딩, 납땜을 모두 알아야 합니다. 이 가이드는 설계 가이드라인만 제공하며, 자세한 학습 자료 링크를 연결해 드립니다.
중요 규제 사항 (미국 FAA 기준)
풍선 발사 전 반드시 확인해야 할 규정들입니다. 거주 국가별로 다를 수 있으니 현지 법규를 확인하세요.
페이로드에 휴대폰 신호가 없어야 함. 휴대폰 탑재 시 비행기 모드 필수.
GPS로 풍선 추적 시 햄 무선 면허 필요 (시험 및 수수료 필요).
페이로드 무게/면적 비율이 3.0 oz/in² 초과 시 총 중량 4파운드 미만.
개별 페이로드/패키지 6파운드 미만.
페이로드 2개 시 총 중량 12파운드 미만.
FAA 전체 가이드라인 (영문)
경로 1 - 프로토 보드 상세
설계 전 고려사항
회로도에는 온습도 센서(DHT22), 기압 센서, IMU(ADXL335), SD 카드 모듈이 포함됩니다. 페이로드 회수를 위한 GPS 모듈 추가도 고려하세요.
회수 방식 옵션
SPOT GPS 모듈: 위성 통신으로 무제한 범위, 배터리 수명 우수, 구하기 쉬움. 단점: 고가, 거꾸로 되면 작동 안 함, 두꺼운 물체에 신호 차단. 짐벌 설계로 수직 유지 권장. (SPOT 3 Satellite Messenger 추천)
APRS 무선: 가장 신뢰성 높음. 아두이노 연결로 데이터·좌표 전송 가능, 고도 측정 정확. 단점: 햄 무선 면허 필수, 배선도 미포함으로 직접 학습 필요.
휴대폰: 비추천. 비행 후 비행기 모드 해제 문제, 가격 비쌈, 백업용으로만 고려.
비행 프로파일 계획
낙하산 원격 전개, 센서 선택(최저 -40°C 고려), 전원 공급 방식(저온에서 배터리 성능 저하 대비 대용량 배터리 + 스티로폼 보온함), 무게 제한 준수 등을 체크리스트로 정리하세요. 페이로드 베이 제작 후 데이터 로거를 설계하는 순서도 좋습니다.
회로도 및 배선도
아두이노 메가는 오버스펙이지만 제공 코드 사용 시 필수. 나노 등 다른 보드 사용 시 SD 카드 모듈 핀아웃을 해당 보드의 SPI 핀으로 변경하세요.
부품 리스트
SparkFun ADXL337
헤더 핀
마이크로 SD 카드 모듈
DHT22 습도 센서
DS18B20 온도 센서
프로토 보드
Arduino Mega
내부 온도 측정용 DS18B20 추가 가능. 제공 코드 사용 시 위 배선도 준수.
아두이노 코드 (경로 1)
// DHT22 설정
#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// SD 카드 설정
#include
#include
File sdcard_file;
int CS_pin = 10;
// DS18B20 온도 센서
#include
#include
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
int scale = 3; // ADXL337용 ±3g, ADXL377용 ±200g
boolean micro_is_5V = true; // 5V 마이컨이면 true, 3.3V면 false
void setup() {
Serial.begin(9600);
sensors.begin();
dht.begin();
pinMode(CS_pin, OUTPUT);
if (SD.begin()) {
Serial.print("SD Card found!");
} else {
Serial.print("Error finding SD Card");
return;
}
sdcard_file = SD.open("data.csv", FILE_WRITE);
if (sdcard_file) {
// 헤더 작성
sdcard_file.print("DS18B20");
sdcard_file.print(",");
sdcard_file.print("DHT22 Temp");
sdcard_file.print(",");
sdcard_file.print("Hum");
sdcard_file.print(",");
sdcard_file.print("H.InX");
sdcard_file.print(",");
sdcard_file.print("rawX");
sdcard_file.print(",");
sdcard_file.print("rawY");
sdcard_file.print(",");
sdcard_file.print("rawZ");
sdcard_file.print(",");
sdcard_file.print("scaledX");
sdcard_file.print(",");
sdcard_file.print("scaledY");
sdcard_file.print(",");
sdcard_file.print("scaledZ");
sdcard_file.print(",");
sdcard_file.println(" ");
} else {
Serial.print("error initializing SD card");
}
}
void loop() {
delay(222);
// 가속도계
int rawX = analogRead(A0);
int rawY = analogRead(A1);
int rawZ = analogRead(A2);
float scaledX, scaledY, scaledZ;
if (micro_is_5V) {
scaledX = mapf(rawX, 0, 675, -scale, scale);
scaledY = mapf(rawY, 0, 675, -scale, scale);
scaledZ = mapf(rawZ, 0, 675, -scale, scale);
} else {
scaledX = mapf(rawX, 0, 1023, -scale, scale);
scaledY = mapf(rawY, 0, 1023, -scale, scale);
scaledZ = mapf(rawZ, 0, 1023, -scale, scale);
}
// DS18B20 온도
sensors.requestTemperatures();
int etemp = sensors.getTempCByIndex(0);
// DHT22 습도/온도
float h = dht.readHumidity();
float t = dht.readTemperature();
float f = dht.readTemperature(true);
if (isnan(h) || isnan(t) || isnan(f)) {
return;
}
float hif = dht.computeHeatIndex(f, h);
float hic = dht.computeHeatIndex(t, h, false);
// 데이터 기록
sdcard_file.print(etemp);
sdcard_file.print(",");
sdcard_file.print(t);
sdcard_file.print(",");
sdcard_file.print(h);
sdcard_file.print(",");
sdcard_file.print(hic);
sdcard_file.print(",");
sdcard_file.print(rawX);
sdcard_file.print(",");
sdcard_file.print(rawY);
sdcard_file.print(",");
sdcard_file.print(rawZ);
sdcard_file.print(",");
sdcard_file.print(scaledX);
sdcard_file.print(",");
sdcard_file.print(scaledY);
sdcard_file.print(",");
sdcard_file.print(scaledZ);
sdcard_file.print(",");
sdcard_file.print(" ");
sdcard_file.println(" ");
sdcard_file.close();
delay(223);
}
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
중요 팁
배선 확인 및 개별 센서 테스트 코드로 문제 분리
SD 카드는 반드시 FAT16 또는 FAT32로 포맷, 16GB 이하 사용
상태 표시용 LED 배선 권장 (데이터 로거 작동 확인용)
SD 카드 모듈 문제 시 전원 공급원 변경 테스트 (전압에 민감한 모듈 존재)
고도 측정은 GPS 필수. 기압계 기반 고도계는 30,000ft(약 9km) 이상에서 90% 작동 안 함. 풍선은 보통 이 고도 초과하므로 기압계만으론 불가.
경로 2 - 사전 설계된 PCB (Eclipse PCB)
내부/외부 온도, 습도, 가속도 측정 가능. 납땜 필요. 부품: 5050 SMD LED, ADXL337, 헤더 핀 다수, 마이크로 SD 모듈, DHT22, DS18B20 2개, 아두이노 메가, Eclipse PCB.
거버 파일은 GitHub 저장소 에서 다운로드. PCB 주문 시 설정: 레이어/치수 유지, 단일 PCB, 1.6mm 두께, 색상 자유(녹색 빠름), HASL(납 포함), 1oz 구리, 골드 핑거 없음, 플라잉 프로브 테스트, 캐스텔레이티드 홀 없음, 수량 자유. JLC PCB 등 중국 업체 이용 시 $2~5 수준.
PCB 조립 가이드
헤더 핀을 PCB 양쪽에 납땜하면 아두이노 메가 위에 실드처럼 장착되어 배선 자동 연결. PCB 내부 0.35mm 미세 배선이 센서-헤더 핀-아두이노 연결. 프로토보드/브레드보드보다 견고하고 신뢰성 높음.
코드 업로드 및 테스트
GitHub 저장소의 1.6 코드 사용. 마이크로 SD 카드 FAT16/32 포맷, 16GB 이하. 2GB면 2시간 비행 충분. 업로드 후 시리얼 모니터에서 SD 카드 성공 메시지 확인, 데이터 로깅 즉시 시작. 10초 후 카드 꺼내 CSV 파일 확인.
아두이노 코드 (경로 2 - 기압계 MPL3115A2 추가)
// DHT22 설정
#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// SD 카드 설정
#include
#include
File sdcard_file;
int CS_pin = 10;
// DS18B20 온도 센서
#include
#include
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// MPL3115A2 고도계 설정
#include
#include
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();
int scale = 3;
boolean micro_is_5V = true;
// LED 핀
int Blue = 7;
int Red = 6;
int Green = 5;
void setup() {
pinMode(23, INPUT); // 스위치로 SD 기록 중지 감지
sensors.begin();
pinMode(Blue, OUTPUT);
pinMode(Red, OUTPUT);
pinMode(Green, OUTPUT);
digitalWrite(Green, HIGH);
dht.begin();
pinMode(CS_pin, OUTPUT);
if (SD.begin()) {
// 녹색 LED 3회 깜빡임으로 성공 표시
for(int i=0;i<3;i++) {
digitalWrite(Green, LOW); delay(100);
digitalWrite(Green, HIGH); delay(100);
}
} else {
digitalWrite(Green, LOW);
digitalWrite(Red, HIGH);
delay(5000);
return;
}
sdcard_file = SD.open("data.csv", FILE_WRITE);
if (sdcard_file) {
digitalWrite(Red, LOW);
digitalWrite(Blue, LOW);
digitalWrite(Green, HIGH);
} else {
// 빨강/파랑 번갈아 깜빡임으로 에러 표시
for(int i=0;i<2;i++) {
digitalWrite(Green, LOW); digitalWrite(Red, HIGH); delay(1000);
digitalWrite(Red, LOW); digitalWrite(Blue, HIGH); delay(1000);
}
digitalWrite(Red, HIGH); digitalWrite(Blue, LOW);
}
// CSV 헤더
sdcard_file.print(",");
sdcard_file.print("DS18B20");
sdcard_file.print(",");
sdcard_file.print("DHT22 Temp");
sdcard_file.print(",");
sdcard_file.print("Hum");
sdcard_file.print(",");
sdcard_file.print("H.InX");
sdcard_file.print(",");
sdcard_file.print("Int. Temp");
sdcard_file.print(",");
sdcard_file.print("Pres. Pasc.");
sdcard_file.print(",");
sdcard_file.print("alt.");
sdcard_file.print(",");
sdcard_file.print("rawX");
sdcard_file.print(",");
sdcard_file.print("rawY");
sdcard_file.print(",");
sdcard_file.print("rawZ");
sdcard_file.print(",");
sdcard_file.print("scaledX");
sdcard_file.print(",");
sdcard_file.print("scaledY");
sdcard_file.print(",");
sdcard_file.print("scaledZ");
}
void loop() {
delay(222);
digitalWrite(Green, LOW);
digitalWrite(Blue, HIGH);
if(! baro.begin()) { }
float pascals = baro.getPressure();
float altm = baro.getAltitude();
float tempC = baro.getTemperature();
// 가속도계
int rawX = analogRead(A0);
int rawY = analogRead(A1);
int rawZ = analogRead(A2);
float scaledX, scaledY, scaledZ;
if (micro_is_5V) {
scaledX = mapf(rawX, 0, 675, -scale, scale);
scaledY = mapf(rawY, 0, 675, -scale, scale);
scaledZ = mapf(rawZ, 0, 675, -scale, scale);
} else {
scaledX = mapf(rawX, 0, 1023, -scale, scale);
scaledY = mapf(rawY, 0, 1023, -scale, scale);
scaledZ = mapf(rawZ, 0, 1023, -scale, scale);
}
// DS18B20 온도
sensors.requestTemperatures();
// DHT22
float h = dht.readHumidity();
float t = dht.readTemperature();
float f = dht.readTemperature(true);
if (isnan(h) || isnan(t) || isnan(f)) {
return;
}
float hif = dht.computeHeatIndex(f, h);
float hic = dht.computeHeatIndex(t, h, false);
// 데이터 기록
sdcard_file.println(sensors.getTempCByIndex(0));
sdcard_file.print(",");
sdcard_file.print(t);
sdcard_file.print(",");
sdcard_file.print(h);
sdcard_file.print(",");
sdcard_file.print(hic);
sdcard_file.print(",");
sdcard_file.print(tempC);
sdcard_file.print(",");
sdcard_file.print(pascals);
sdcard_file.print(",");
sdcard_file.print(altm);
sdcard_file.print(",");
sdcard_file.print(rawX);
sdcard_file.print(",");
sdcard_file.print(rawY);
sdcard_file.print(",");
sdcard_file.print(rawZ);
sdcard_file.print(",");
sdcard_file.print(scaledX);
sdcard_file.print(",");
sdcard_file.print(scaledY);
sdcard_file.print(",");
sdcard_file.print(scaledZ);
sdcard_file.print(",");
delay(223);
sdcard_file.close();
}
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
경로 3 - 직접 PCB 설계 가이드라인
이 가이드는 PCB 설계 방법을 가르치지 않습니다. PCB 설계 경험이 있거나 학습 후 진행하세요. 미국 기준 가이드라인입니다.
비행 데이터 요구사항 정의
일반적 측정 항목: 온도, 습도, 고도, 공기질, 방사선, GPS. 온도 센서는 -50°C(-58°F)까지 측정 가능하고 정확도 높은 것 선택. 태양 복사로 고온에서 온도 측정값이 실제보다 높게 나올 수 있음 주의.
회수 방식 선택 (경로 1과 동일)
SPOT GPS, APRS 무선(햄 면허 필요), 휴대폰(비추천) 중 선택. SPOT 3 + 3D프린트 짐벌 추천.
설계 고려사항
무게 제한: 페이로드 유형별 12lb/6lb/4lb 준수
고도 측정: 기압계 30,000ft 이상 불가 → GPS 필수
전원 대책: 성층권 초저온에서 배터리/아두이노 실패 확률 높음. 스티로폼 박스로 실내 온도 유지, 태양광 패널 또는 핫팩 병행 고려
회로도 및 PCB 설계
실드형 또는 독립형 PCB 중 선택. 설계 후 모든 동작 검증 및 규정 준수 재확인 필수.
결론
기상 관측 풍선은 성층권으로 장비(360 카메라 등)를 보내 영상과 데이터를 얻는 멋진 방법입니다. 규제가 비교적 적지만 반드시 규칙 준수 하세요.
필독!
레이더 반사판 부착 및 NOTAM(항공고시보) 제출 필수
페이로드가 항공기와 충돌할 가능성 존재 → 인명/재산 피해 방지 위해 모든 예방 조치
모든 것을 견고하게 고정해 낙하물 방지
FAA 규제가 많지 않다고 방심하지 말고 자발적 준수로 모두의 기회 보호
이 가이드가 도움이 되길 진심으로 바랍니다. 유튜브 채널 구독과 댓글로 응원해 주세요. 질문 언제든 환영합니다!
즐거운 아두이노 생활! (이런 말이 있나?)
NM Rocketry Reviews
커스텀 파트 및 인클로저
SPOT 퍼스널 트래커를 비행 중 수직 유지하기 위한 짐벌 STL 파일 제공.
회로도
경로 1용 배선도 및 참고 이미지. GitHub 저장소 에서 PCB 거버 파일과 코드 다운로드 가능.