Community project

ESP32 Metal Detector

ESP32
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

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