Introduction: How to Build a DIY Walkthrough Detector Using Arduino UNO

About: We are a team of software and hardware engineers with 30 years of combined experience. At FG Sensors our main focus are fluxgate magnetometers, custom hardware, software solutions & research.

This project demonstrates a low-cost and sensitive walkthrough metal detector using the FG-3+ fluxgate magnetometer sensor paired with the Arduino UNO R4 WiFi. The device detects the presence of metal objects based on perturbations in the ambient magnetic field, a matrix display shows an exclamation mark "!" whenever a metal object is detected near the walkthrough detector.


Supplies

Step 1: About Fluxgate Magnetometers

Fluxgate magnetometers like the FG-3+ sensor offer high sensitivity and low noise level for detecting minute changes in magnetic fields. They are commonly used in geophysics and in security applications such as metal detection because they can precisely sense the magnetic disturbances caused by metallic objects.

The FG-3+ sensor by www.fgsensors.com provides stable and accurate magnetic field measurements and is well-suited for embedded platforms like Arduino, enabling development of compact, portable detection systems.

Step 2: Hardware Setup

  1. Position the FG-3+ sensor near the detector's walkthrough frame to sense metallic objects passing through.
  2. Connect the FG-3+ sensor signals to Arduino UNO R4 WiFi

Step 3: Firmware Functionality

Sensor Reading:

The sensor outputs a frequency proportional to the local magnetic field (typically from 40 kHz to 120 kHz). This frequency is measured using interrupt-driven counting logic.

Signal Filtering:

A moving average baseline is maintained to track ambient field conditions and smooth sensor output to avoid false triggers.

Metal Detection:

Sudden deviations beyond a configurable threshold relative to the baseline indicate a metal object presence.

Visual Alert:

The LED matrix displays "!" during detection events.

Step 4: Code

#include <Arduino_LED_Matrix.h>


const int sensorDig = 2; // Digital pin 2 for sensor input (interrupt pin)


unsigned int updateRate = 100; // Sensor update rate in ms

unsigned int measureTime = 100; // Sensor measure time in ms


volatile unsigned int intEnable = 0; // Interrupt counter enable flag

volatile unsigned long sensorDigCnt = 0; // Count of sensor signal changes

unsigned long prevMillis = 0; // For timing updates


float base_line = 0.0; // Moving average baseline

const float alpha = 0.1; // Smoothing factor for baseline

const float base_line_change = 10.0; // Threshold for significant change

const int N = 10; // Number of samples for peak event duration


int peakSamplesCount = 0; // Samples counted during peak event

bool inPeakEvent = false; // Flag: tracking a peak event?

float peakValue = 0.0; // Maximum deviation recorded during peak event


int state = 0; // State machine variable


// LED matrix setup

ArduinoLEDMatrix matrix;


const int matrixWidth = 12;

const int matrixHeight = 8;


// Alert sign bitmap 10x7 pixels (largest fitting design)

// A large exclamation mark ! with border

const uint8_t alertWidth = 10;

const uint8_t alertHeight = 7;

const uint8_t alertBitmap[alertHeight] = {

0b0000110000, // border top

0b0000110000, // | ! |

0b0000110000, // | ! |

0b0000110000, // | ! |

0b0000000000, // | ! |

0b0000110000, // | ! |

0b0000110000 // border bottom

};


uint8_t frame[matrixHeight][matrixWidth];


void clearFrame() {

for (int y = 0; y < matrixHeight; y++) {

for (int x = 0; x < matrixWidth; x++) {

frame[y][x] = 0;

}

}

}


void drawAlert(int xPos, int yPos) {

clearFrame();

for (int row = 0; row < alertHeight; row++) {

for (int col = 0; col < alertWidth; col++) {

bool pixelOn = alertBitmap[row] & (1 << (alertWidth - 1 - col));

int x = xPos + col;

int y = yPos + row;

if (pixelOn && x >= 0 && x < matrixWidth && y >= 0 && y < matrixHeight) {

frame[y][x] = 1;

}

}

}

matrix.renderBitmap(frame, matrixHeight, matrixWidth);

}


void setup() {

Serial.begin(115200);

pinMode(sensorDig, INPUT);

attachInterrupt(digitalPinToInterrupt(sensorDig), sensorDigHandler, RISING);


matrix.begin();

matrix.clear();


// Initialize baseline with first measurement

sensorDigCnt = 0;

intEnable = 1;

delay(measureTime);

intEnable = 0;

base_line = sensorDigCnt;

Serial.print("Initial baseline: ");

Serial.println(base_line);


prevMillis = millis();

}


void loop() {

unsigned long currMillis = millis();


switch (state) {

case 1:

// Update baseline (moving average)

base_line = alpha * sensorDigCnt + (1.0 - alpha) * base_line;


// Calculate deviation from baseline

float diff = sensorDigCnt - base_line;


if (!inPeakEvent) {

// Not currently tracking a peak event

if (abs(diff) > base_line_change) {

inPeakEvent = true;

peakSamplesCount = 1;

peakValue = diff;

}

} else {

// Tracking peak event

peakSamplesCount++;

if (abs(diff) > abs(peakValue)) {

peakValue = diff;

}


// End peak detection when signal returns near baseline or max samples reached

if (abs(diff) <= base_line_change || peakSamplesCount > N) {

Serial.print("Peak detected: ");

Serial.println(peakValue);

inPeakEvent = false;

peakSamplesCount = 0;

peakValue = 0.0;


// Display alert sign centered on the matrix

drawAlert((matrixWidth - alertWidth) / 2, (matrixHeight - alertHeight) / 2);

delay(2000); // Display alert for 2 seconds

matrix.clear();

}

}


Serial.print("Sensor= ");

Serial.print(sensorDigCnt);

Serial.print(" Baseline= ");

Serial.println(base_line);


state = 0;

break;

}


if (currMillis - prevMillis >= updateRate) {

sensorDigCnt = 0;

prevMillis = currMillis;

intEnable = 1;

delay(measureTime);

intEnable = 0;

state = 1;

}

}


// Interrupt handler increments counter on rising edge

void sensorDigHandler() {

if (intEnable == 1) {

sensorDigCnt++;

}

}

Step 5: How the Code Works

Interrupt Counting:

Each rising edge on the sensor output increments a counter during a fixed measurement window.

Baseline Tracking:

The system establishes a baseline frequency count during startup representing no metal present. This baseline is continuously updated slowly to adapt to environmental changes.

Peak Detection:

When sensor readings deviate beyond a threshold for a sustained period, a detection event is flagged.

Display Update:

On detection, the exclamation mark is shown on the matrix display to alert users.

Parameter Recommendations

Step 6: Testing the Detector

Test by moving a metal object (like keys) near the sensor. The exclamation mark should appear on the matrix indicating detection.

Note: The FG-3+ sensor is highly sensitive and may respond to electromagnetic interference in your environment, so calibrate thresholds carefully.

Step 7: Suggested Improvements - Multi-Sensor Array Using AI

  1. Deploy several FG-3+ sensors spaced across the walkthrough frame.
  2. Use their comparative outputs to detect and localize metal objects precisely.
  3. Combine readings to filter out false positives caused by environmental EMI (which may only affect some sensors non-simultaneously).
  4. This approach enhances sensitivity, positional awareness, and reduces false alarms.
  5. Use AI to identify different objects

Step 8: Applications

  1. Security checkpoints for metal detection without bulky equipment.
  2. Museum exhibits to monitor access or protect sensitive artifacts.
  3. Industrial safety where metal detection is needed in restricted zones

Step 9: Conclusion

This walkthrough metal detector design harnesses the precision of FG-3+ fluxgate magnetometers and the versatile Arduino UNO R4 WiFi to create a cost-effective and cloud-enabled metal detection system suitable for diverse security and monitoring needs.