아두이노 나노 펄스 유도 금속 탐지기 – DIY 키트 및 가이드
부품 및 소모품
| 부품 |
수량 |
| 아두이노 나노 R3 (Arduino Nano R3) |
1 |
| HC-05 블루투스 모듈 |
1 |
| 범용 PNP 트랜지스터 |
1 |
| 범용 NPN 트랜지스터 |
1 |
| 파워 MOSFET N-채널 |
1 |
| 오페라셔널 앰프 (Op Amp + 비교기 + 레퍼런스) |
1 |
| 1N4148 고속 스위칭 다이오드 |
2 |
| 커패시터 10µF |
1 |
| 커패시터 1µF |
1 |
| 단일 회전 포텐쇼미터 100k옴 |
1 |
| 단일 회전 포텐쇼미터 47k옴 |
1 |
| 각기 다른 값의 저항 12개 |
1세트 |
필요 도구
프로젝트 소개
펄스 유도(PI) 시스템은 단일 코일을 송신기와 수신기로 동시에 사용합니다. 강력한 짧은 전류 펄스를 코일에 보내 자기장을 생성하고, 펄스가 끝나면 자기장이 급격히 붕괴되면서 날카로운 전기 스파이크(반사 펄스)가 발생합니다. 이 반사 펄스는 약 30마이크로초 지속되며, 다시 펄스를 보내는 과정을 반복합니다. 금속이 자기장 범위 내에 들어오면 수신 코일에서 진폭과 위상 변화를 감지해 금속의 크기, 거리, 철/비철 여부를 판별할 수 있습니다.
N.E.C.O. 프로젝트 사이트에서 좋은 PI 탐지기 예제를 찾았습니다. 이 금속 탐지기는 아두이노와 안드로이드의 결합체입니다. 플레이스토어에서 무료 앱 'Spirit PI'(기능 제한 없음, 프로 버전도 있음)를 다운로드받아 사용합니다. 스마트폰과 아두이노 간 통신은 HC-05 블루투스 모듈로 하며, 보드레이트는 115200으로 설정해야 합니다. 원본 회로도는 N.E.C.O. 웹사이트에 있습니다.
수정 사항
- 원본 150옴 저항 대신 47k옴 트리머 포텐쇼미터 사용 → 코일 전류 조절로 감도 조절 가능
- 원본 62k 저항 대신 100k옴 트리머 포텐쇼미터 사용 → 아두이노 A0 입력 전압을 약 4.5V로 설정 (오페라셔널 앰프 및 전압에 따라 최적값 상이)
- 전원: 리튬 이온 배터리 4개를 직렬 연결(15V 이상) → 아두이노 최대 입력 12V이므로 7805 레귤레이터(소형 히트싱크 장착)로 5V 강압 후 아두이노 +5V 핀에 직접 공급
코일 제작
직경 0.4mm 에나멜 동선으로 원형 코일(지름 19cm) 25턴 감기. 최종 조립 시 코일 근처에 금속 물체(나사 등)가 없도록 접착제로 고정해야 합니다.
성능
동영상 기준으로 소형 동전 10~15cm, 큰 금속 물체 30~40cm 이상 거리에서 탐지됩니다. 제작과 설정이 비교적 간단한 점을 고려하면 훌륭한 결과입니다.
코드
#include <EEPROM.h>
// #include <SoftwareSerial.h>
// int bluetoothTx = 5; int bluetoothRx = 6;
// SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);
#define pulsePin 2
int timer = 200;
const int button1 = 12;
int buttonState1 = 0;
char data;
// EEPROM 변수
int addr_duty = 0;
int addr_freq = 1;
int stored_value;
int duty_cycle, duty_cycle_temp;
int freq, freq_temp;
int duty_def_value = 10;
int freq_def_value = 60;
// 밸런스 변수
int value_count = 0;
int value_count_def = 100;
int balance_value = 0;
int balance_value_temp = 0;
// 타이밍
unsigned long startMillis, currentMillis;
long period = 100000; // 마이크로초 단위
// 배터리 전압 측정
float resistencia1 = 101000; // 100k
float resistencia2 = 10000; // 10k
float const arefVolt = 4.8f; // 정확한 값으로 설정
float voutv, vinv;
unsigned long startMillisVolts, currentMillisVolts;
long periodVolts = 2000;
int sensorValue;
void setup() {
// analogReference(INTERNAL);
Serial.begin(115200);
readFromStorage(addr_duty); duty_cycle = stored_value;
readFromStorage(addr_freq); freq = stored_value;
if (duty_cycle == 0 || duty_cycle == 255) {
writeToStorage(duty_def_value, addr_duty);
readFromStorage(addr_duty); duty_cycle = stored_value;
}
if (freq == 0 || freq == 255) {
writeToStorage(freq_def_value, addr_freq);
readFromStorage(addr_freq); freq = stored_value;
}
pinMode(pulsePin, OUTPUT);
}
void loop() {
currentMillis = micros();
currentMillisVolts = millis();
if (Serial.available() > 0) {
data = Serial.read();
setDutyAndFreq(data);
}
if (currentMillis - startMillis >= period) {
period = 1000000 / freq;
digitalWrite(pulsePin, HIGH);
duty_cycle_temp = duty_cycle * 10;
delayMicroseconds(duty_cycle_temp);
digitalWrite(pulsePin, LOW);
for (int i = 0; i < 3; i++) sensorValue = analogRead(A0);
delayMicroseconds(10);
sensorValue = analogRead(A0);
sensorValue = sensorValue / 5;
sendData();
startMillis = currentMillis;
}
// 전압 측정
if (currentMillisVolts - startMillisVolts >= periodVolts) {
lecturaVoltios();
startMillisVolts = currentMillisVolts;
}
}
void writeToStorage(int valor, int addr) { EEPROM.write(addr, valor); }
int readFromStorage(int addr) { stored_value = EEPROM.read(addr); return stored_value; }
void setDutyAndFreq(char valor) {
// 'n': 듀티 사이클 증가, 'm': 감소
// 'j': 주파수 증가, 'k': 감소
// '+': 밸런스 증가, '-': 감소
// 'p': 기본값으로 리셋
if (valor == 'n') { readFromStorage(addr_duty); duty_cycle = stored_value + 1; writeToStorage(duty_cycle, addr_duty); }
else if (valor == 'm') { readFromStorage(addr_duty); duty_cycle = stored_value - 1; writeToStorage(duty_cycle, addr_duty); }
else if (valor == 'j') { readFromStorage(addr_freq); freq = stored_value + 10; writeToStorage(freq, addr_freq); }
else if (valor == 'k') { readFromStorage(addr_freq); freq = stored_value - 10; writeToStorage(freq, addr_freq); }
else if (valor == 'p') { writeToStorage(0, addr_freq); writeToStorage(0, addr_duty); }
}
void lecturaVoltios() {
vinv = 0.0f; voutv = 0.0f;
for (int i = 0; i < 100; i++) {
voutv = (analogRead(A7) * arefVolt) / 1023;
vinv += voutv / (resistencia2 / (resistencia1 + resistencia2));
if (vinv < 0.9) vinv = 0.0f;
}
vinv = vinv / 100;
}
void sendData() {
String data = "<";
data += sensorValue; data += "/";
data += freq; data += "/";
data += duty_cycle; data += "/";
data += vinv; data += ">";
Serial.println(data);
// 블루투스 전송 코드는 주석 처리됨
}
회로도
회로도 이미지 참조