아두이노와 스마트폰으로 만드는 스마트 전압계 – 초간단 DIY 프로젝트
부품 및 소모품
부품
수량
아두이노 우노 (Arduino UNO)
1
아두이노용 PHPoC 와이파이 실드
1
저항 100kΩ
1
저항 1MΩ
1
점퍼 와이어
1세트
프로젝트 개요
아두이노 초보자라면 아두이노 초보자 튜토리얼 로 시작해 보세요.
작동 원리
아두이노의 아날로그 입력 핀으로 전압을 측정할 수 있지만, 최대 측정 가능 전압은 5V로 제한됩니다. 더 높은 전압을 측정하려면 분압기(Voltage Divider) 회로가 필요합니다.
이론적 계산
분압비는 다음과 같이 계산됩니다:
V_measure = (R1 + R2) / R2 × V_in
ratio = (R1 + R2) / R2
R1 = 10 × R2로 설정하면 ratio = 11이 되어, 최대 측정 전압은 5V × 11 = 55V 가 됩니다. 아두이노 핀으로 흐르는 전류를 제한해 손상을 방지하려면 저항값을 최대한 크게 선택해야 합니다. 본 프로젝트에서는 R1 = 1MΩ, R2 = 100kΩ 을 사용했습니다.
실제 구현 시 고려사항
저항의 실제 오차로 인한 측정 오차 발생
아두이노 전원 공급 불안정으로 인한 아날로그 기준 전압(V_REF) 변동
이러한 문제는 캘리브레이션(보정) 과정을 통해 해결합니다.
캘리브레이션 방법
1. 실제 분압비 측정 (1회만 수행)
ratio = (R1 + R2) / R2 = V_measure / V_in = A1_read_value / A0_read_value
아두이노의 A0, A1 핀을 이용해 측정된 아날로그 값의 비율로 실제 분압비를 구합니다. 코드는 ResistorCalibration.ino를 참조하세요.
2. 전원 공급 불안정 보정
아두이노의 3.3V 핀을 A1에 연결해 기준 전압을 간접 측정합니다:
V_reference = 1023 × 3.3 / A1_value
V_in = mapFloat(A0_value, 0, 1023, 0, V_reference)
V_measure = ratio × V_in
제작 순서
"저항 캘리브레이션 배선"대로 회로 구성
ResistorCalibration.ino 업로드
시리얼 플로터에서 ratio 값 확인 및 복사
Voltmeter.ino 5번째 줄의 ratio 값 교체
"메인 배선"대로 회로 재구성
Voltmeter.ino 업로드
웹 UI(voltmeter.php, voltmeter_body.jpg)를 PHPoC 실드에 업로드
브라우저에서 https://phpoc_shield_ip/voltmeter.php 접속해 전압 확인
배선 팁
코드
ResistorCalibration.ino (아두이노)
double ratio;
int analogReadAverage(int pin, int read_time) {
unsigned long read_value = 0;
for(int i = 0; i < read_time; i++) {
read_value += analogRead(pin);
delay(2);
}
return read_value / read_time;
}
double resistorCalibration() {
double _ratio;
double A0_read_value = analogReadAverage(A0, 20);
double A1_read_value = analogReadAverage(A1, 20);
_ratio = A1_read_value / A0_read_value;
return _ratio;
}
void setup() {
Serial.begin(9600);
}
void loop() {
ratio = resistorCalibration();
Serial.print("ratio:");
Serial.println(ratio);
delay(200);
}
Voltmeter.ino (아두이노 메인 코드)
double V_measure, V_in, V_reference;
double ratio = 11.59; // 캘리브레이션 값으로 교체
double mapFloat(double x, double in_min, double in_max, double out_min, double out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
int analogReadAverage(int pin, int read_time) {
unsigned long read_value = 0;
for(int i = 0; i < read_time; i++) {
read_value += analogRead(pin);
delay(2);
}
return read_value / read_time;
}
double getVoltageReference() {
double A1_value = analogReadAverage(A1, 20);
return 1023 * 3.3 / A1_value;
}
void setup() {
Serial.begin(9600);
}
void loop() {
V_reference = getVoltageReference();
double A0_value = analogReadAverage(A0, 20);
V_in = mapFloat(A0_value, 0, 1023, 0, V_reference);
V_measure = ratio * V_in;
Serial.println(V_measure);
delay(200);
}
voltmeter.php (웹 UI)
<!DOCTYPE html>
<html>
<head>
<title>PHPoC Shield - VOLTMETER</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<link rel="stylesheet" media="screen" href="https://fontlibrary.org/face/segment7" type="text/css"/>
<style>
html {height: 100%;}
body {width: 100%; height: 100%; text-align: center;}
.container {width: 100%; min-height: 99%; max-height: 100%;}
canvas {
background: url(voltmeter_body.jpg);
background-size: contain;
font-family: 'Segment7Standard';
font-weight: bold;
font-style: italic;
}
</style>
<script>
// 웹소켓 통신 및 캔버스 렌더링 코드
// (원본 코드 참조)
</script>
</head>
<body onresize="canvas_resize()">
<div class="container" id="container">
<canvas id="graph"></canvas>
</div>
</body>
</html>
회로도