Community project
ESP32 Metal Detector
This ESP32-based metal detector uses electromagnetic induction to sense ferrous and conductive materials. The circuit generates a high-frequency pulse through a search coil, then measures the decay of the resulting magnetic field using an op-amp comparator and Schmitt-trigger logic. When metal enters the field, it dampens the decay, triggering an LED and piezo buzzer alarm.
This guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for building the detector on a breadboard. The included Arduino firmware handles coil driving, signal sampling, baseline calibration, and threshold detection. Builders will learn how inductive sensing works and gain a functional metal detector ready for field testing.
Wiring diagram

Gather all the parts
Assemble it in 8 steps
1. Alimenta la placa de pruebas
Con el ESP32 desconectado del USB, lleva 3V3 del ESP32 al carril rojo de la placa de pruebas y GND al carril azul. Todos los circuitos deben compartir ese mismo carril de tierra.
- Usa cables rojos para 3V3 y negros para GND para encontrar errores con facilidad.
- No conectes 5 V a los integrados ni a GPIO34: una señal de 5 V puede dañar el ESP32.
2. Coloca y alimenta los dos integrados
Pon el MCP602 y el 74HC14D atravesando la ranura central de la placa de pruebas. Conecta VDD del MCP602 y VCC del 74HC14D a 3V3 (alimentación), y VSS del MCP602 y GND del 74HC14D a GND (tierra). Coloca cap_mcp_1 y cap_hc14_1: cada condensador va entre 3V3 y GND, pegado a su integrado.
- El punto o muesca del encapsulado marca el extremo de la pata 1; consulta el dibujo de pines del fabricante antes de colocar cada chip.
- Una fila desplazada o alimentar el chip al revés puede calentarlo y dañarlo.
3. Termina las entradas que no se usan
En el MCP602, une OUTA con INA− y conecta OUTA a GPIO34 (señal). Une OUTB con INB− y conecta INB+ a GND para que el segundo amplificador no quede suelto. En el 74HC14D, conecta las entradas 2A, 3A, 4A, 5A y 6A a GND; deja sus salidas sin conectar.
- Una entrada suelta puede captar ruido y hacer que el montaje consuma de más o se comporte de forma errática.
4. Cablea la bobina y el transistor
Conecta coil_1 L1 a 3V3 (alimentación). Conecta coil_1 L2 al drenador D de mosfet_1 (señal). Conecta la fuente S del MOSFET a GND (tierra). Conecta diode_1 entre el mismo punto L2/D y 3V3: el extremo con raya, K, va a 3V3 (protección) y el otro extremo, A, va a L2/D (protección).
- Puedes hacer la bobina con unas 35 vueltas de hilo de cobre esmaltado de 0,4 a 0,6 mm sobre un aro no metálico de unos 10 cm. Raspa el esmalte de las dos puntas antes de insertarlas.
- No pongas la bobina sobre una mesa metálica ni uses un aro metálico: el propio soporte activará el detector.
5. Añade el control de la bobina
Conecta GPIO25 a 1A del hc14_1 (señal). Conecta 1Y del hc14_1 a un extremo de r_gate_1, y el otro extremo de r_gate_1 a G de mosfet_1 (señal). Conecta r_pulldown_1 entre G de mosfet_1 y GND (mantiene apagada la bobina durante el arranque).
- El MOSFET AO3400A en formato SOT-23 es muy pequeño; sigue su dibujo de patas del vendedor o usa un adaptador SOT-23 a placa de pruebas.
- Confundir las patas G, D y S del MOSFET puede dejar la bobina encendida y calentar el transistor.
6. Lleva la señal protegida al ESP32
Conecta r_div_top_1 entre el punto común coil_1 L2 / mosfet_1 D y el punto de lectura. Conecta r_div_bottom_1 entre ese punto de lectura y GND. Conecta el punto de lectura a INA+ del mcp602_1; la salida OUTA del MCP602 ya va a GPIO34 (señal).
- Mantén estos cables cortos y separados del cable de GPIO25 para que la lectura tenga menos ruido.
- No conectes el punto de la bobina directamente a GPIO34: los picos de la bobina podrían dañar la entrada del ESP32.
7. Conecta la luz y el zumbador
Conecta GPIO26 a un extremo de r_led_1 y el otro extremo a la pata larga A de led_1 (luz). Conecta la pata corta K del LED a GND (tierra). Conecta el terminal + de piezo_1 a GPIO27 (sonido) y el terminal − a GND (tierra).
- Si el LED no enciende, gira solo el LED; su lado plano y pata corta van a GND.
- No quites r_led_1: sin esa resistencia el LED puede dañarse.
8. Enciende y calibra lejos del metal
Revisa que no haya hilos sueltos, conecta el ESP32 por USB y mantén la bobina lejos de monedas, herramientas, la mesa metálica y tus manos durante unos tres segundos al encender. Después acerca una moneda a la bobina para comprobar que se enciende la luz y suena el zumbador.
- Si detecta siempre o no detecta nada, mueve el valor DETECT_THRESHOLD en el programa: bájalo para más sensibilidad o súbelo para ignorar ruido.
Review all connections
1. Connections between "mcp602_1" and "ESP32"
2. Connections between "hc14_1" and "ESP32"
3. Connections between "coil_1" and "ESP32"
4. Connections between "mosfet_1" and "ESP32"
5. Connections between "diode_1" and "ESP32"
6. Connections between "r_gate_1" and "ESP32"
7. Connections between "r_pulldown_1" and "ESP32"
8. Connections between "r_div_top_1" and "ESP32"
9. Connections between "r_div_bottom_1" and "ESP32"
10. Connections between "cap_mcp_1" and "ESP32"
11. Connections between "cap_hc14_1" and "ESP32"
12. Connections between "led_1" and "ESP32"
13. Connections between "r_led_1" and "ESP32"
14. Connections between "piezo_1" and "ESP32"
Deploy the firmware
#include <Arduino.h>
// Forward declarations
int takeCoilReading();
void setAlarm(bool on);
constexpr int COIL_DRIVE_PIN = 25;
constexpr int LED_PIN = 26;
constexpr int BUZZER_PIN = 27;
constexpr int SENSE_PIN = 34;
constexpr uint32_t PULSE_ON_US = 260;
constexpr uint32_t SETTLE_US = 12;
constexpr uint32_t SAMPLE_INTERVAL_MS = 20;
constexpr int CALIBRATION_SAMPLES = 180;
constexpr float DETECT_THRESHOLD = 35.0f;
float baseline = 0.0f;
float filteredReading = 0.0f;
bool metalPresent = false;
uint32_t lastSampleMs = 0;
int takeCoilReading() {
// The 74HC14 inverts this signal: LOW turns the MOSFET on, HIGH releases it.
digitalWrite(COIL_DRIVE_PIN, LOW);
delayMicroseconds(PULSE_ON_US);
digitalWrite(COIL_DRIVE_PIN, HIGH);
delayMicroseconds(SETTLE_US);
long total = 0;
for (int i = 0; i < 4; ++i) {
total += analogRead(SENSE_PIN);
}
return total / 4;
}
void setAlarm(bool on) {
digitalWrite(LED_PIN, on ? HIGH : LOW);
if (on) {
ledcWriteTone(BUZZER_PIN, 1800);
} else {
ledcWriteTone(BUZZER_PIN, 0);
}
}
void setup() {
Serial.begin(115200);
pinMode(COIL_DRIVE_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
analogReadResolution(12);
analogSetPinAttenuation(SENSE_PIN, ADC_11db);
// Keep the coil unpowered while the ESP32 starts.
digitalWrite(COIL_DRIVE_PIN, HIGH);
setAlarm(false);
delay(300);
long total = 0;
for (int i = 0; i < CALIBRATION_SAMPLES; ++i) {
total += takeCoilReading();
delay(10);
}
baseline = total / (float)CALIBRATION_SAMPLES;
filteredReading = baseline;
Serial.printf("Referencia inicial: %.1f\n", baseline);
}
void loop() {
const uint32_t now = millis();
if (now - lastSampleMs < SAMPLE_INTERVAL_MS) {
return;
}
lastSampleMs = now;
const int raw = takeCoilReading();
filteredReading = filteredReading * 0.78f + raw * 0.22f;
const float change = fabsf(filteredReading - baseline);
metalPresent = change > DETECT_THRESHOLD;
setAlarm(metalPresent);
// Slowly follow temperature and supply changes only when no metal is detected.
if (!metalPresent) {
baseline = baseline * 0.998f + filteredReading * 0.002f;
}
Serial.printf("lectura=%d referencia=%.1f cambio=%.1f %s\n",
raw, baseline, change, metalPresent ? "METAL" : "libre");
}Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.




