Community project

ESP32 Metal Detector

Photo of ESP32 Metal Detector
Generated with AI

Oscar Luis Bustamante Valdivia

Published September 30, 2026

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

Wiring diagram for ESP32 Metal Detector
Loading diagram…

Gather all the parts

QtyComponent
1

MCP602 dual operational amplifier

El pequeño circuito integrado de dos amplificadores que aísla la señal de la bobina antes de llevarla al ESP32.

1

74HC14D hex Schmitt-trigger inverter

El integrado lógico que convierte el pulso del ESP32 en una señal firme para encender y apagar la bobina.

1

Search coil, 330 µH

La bobina de búsqueda que crea el campo magnético y cambia ligeramente cuando acercas metal.

1

AO3400A N-channel logic-level MOSFET

El transistor que conecta rápidamente la bobina a tierra para crear los pulsos de detección.

1

1N5819 Schottky diode

El diodo que limita el pico de la bobina para proteger el transistor y el amplificador.

1

100 Ω resistor

Resistencia en serie que protege la salida lógica al cargar la compuerta del transistor.

1

100 kΩ resistor

Resistencia que mantiene apagado el transistor mientras el ESP32 arranca.

1

68 kΩ resistor

Resistencia superior del divisor que baja la señal de la bobina a un nivel seguro para el MCP602.

1

100 kΩ resistor

Resistencia inferior del divisor que mantiene la entrada del amplificador por debajo de su límite.

1

100 nF ceramic capacitor

Condensador pequeño que limpia la alimentación del MCP602 junto a sus patas de alimentación.

1

100 nF ceramic capacitor

Condensador pequeño que limpia la alimentación del 74HC14D junto a sus patas de alimentación.

1

Green 5 mm LED

La luz que se enciende al detectar una variación grande cerca de la bobina.

1

330 Ω resistor

Resistencia que limita la corriente de la luz LED para que no se dañe.

1

Passive piezo buzzer

El pequeño zumbador que hace un tono cuando la bobina nota metal cercano.

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"

Functionmcp602_1ESP32
powerVDD3V3
groundVSSGND
analogOUTAGPIO 34
analogINA- → MCP602 dual operational amplifier OUTAEXT
analogINA+ → 100 kΩ resistor 1EXT
analogOUTB → MCP602 dual operational amplifier INB-EXT
groundINB+GND

2. Connections between "hc14_1" and "ESP32"

Functionhc14_1ESP32
powerVCC3V3
groundGNDGND
digital1AGPIO 25
digital1Y → 100 Ω resistor 1EXT
ground2AGND
ground3AGND
ground4AGND
ground5AGND
ground6AGND

3. Connections between "coil_1" and "ESP32"

Functioncoil_1ESP32
powerL13V3
dataL2 → AO3400A N-channel logic-level MOSFET DEXT

4. Connections between "mosfet_1" and "ESP32"

Functionmosfet_1ESP32
dataD → 68 kΩ resistor 1EXT
groundSGND

5. Connections between "diode_1" and "ESP32"

Functiondiode_1ESP32
dataA → AO3400A N-channel logic-level MOSFET DEXT
powerK3V3

6. Connections between "r_gate_1" and "ESP32"

Functionr_gate_1ESP32
digital2 → AO3400A N-channel logic-level MOSFET GEXT

7. Connections between "r_pulldown_1" and "ESP32"

Functionr_pulldown_1ESP32
digital1 → AO3400A N-channel logic-level MOSFET GEXT
ground2GND

8. Connections between "r_div_top_1" and "ESP32"

Functionr_div_top_1ESP32
analog2 → 100 kΩ resistor 1EXT

9. Connections between "r_div_bottom_1" and "ESP32"

Functionr_div_bottom_1ESP32
ground2GND

10. Connections between "cap_mcp_1" and "ESP32"

Functioncap_mcp_1ESP32
power13V3
ground2GND

11. Connections between "cap_hc14_1" and "ESP32"

Functioncap_hc14_1ESP32
power13V3
ground2GND

12. Connections between "led_1" and "ESP32"

Functionled_1ESP32
digitalA → 330 Ω resistor 2EXT
groundKGND

13. Connections between "r_led_1" and "ESP32"

Functionr_led_1ESP32
digital1GPIO 26

14. Connections between "piezo_1" and "ESP32"

Functionpiezo_1ESP32
pwm+GPIO 27
ground-GND

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.

Open in Schematik