Arduino Metal Detector Improvements?

Hello, awesome Arduino community!

I am building a simple metal detector for a college class I am in, and am looking for tips/info/advice... anything you all got!

So, here is the original circuit we were told to build:

It is very simple, and is amazing it works at all. After some slight adjustments to the capacitor and resistor R1 values, I was able to improve it a little.
My next improvement is I added a PNP transistor for switching the current through the coil, rather than driving the coil with the Arduino IO pin. This gave it much more current and better results!

Here is my new circuit below (it doesn't show the RGB LED but still has it):

Also as an FYI, the inductor value in that circuit is arbitrary.
The inductor is 4 loops of 20awg solid copper wire, roughly 5-6 inches in diameter.

The 2N3906 is rated for 200mA max, so I calculated the resistor to be 30Ω. If the loop is powered with 9V, then it has to be ~51Ω.

HOW IT WORKS:
The Arduino sends a series of pulses (originally 3, I have 10) through the inductor, which creates electromagnetic fields. The negative spike of the inductor is filtered out with the signal diode and is then used to charge the capacitor in steps. The Arduino reads the voltage of the capacitor, discharges it by setting the pin LOW, and then repeats the cycle again. The capacitor is used to amplify the difference from bringing metal inside the coil's EMF. So, the Arduino sums the voltage measurements over a long period and subtracts the baseline to get a voltage difference.

Does anyone have any input for me on improvements to this circuit!? I would like to power it with a 3s Lithium battery if possible, but I'm not sure if the measurement with the Arduino of the capacitor would be at too high of a voltage.

Thanks in advance!

2 Likes

Nice presentation, and a good upgrade. Good Luck, you should do well!

That should be OK, Just power through Vin. It will not give you the longest battery life but it will be simple to implement.

Consider powering L2 directly from the Vin power source to decouple the inductive pulse from the Arduino. A P-Channel MOSFET would work well for this, but you’ll need to include level translation to properly switch the MOSFET.

Here is a circuit I found on line.
image
If you replace Q1 with a MOSFET, R5 needs to be connected to the port pin not the gate (base).

2 Likes

A refreshing change from the cheaters who want us to write their assignments. All the best.

1 Like

Thanks @gilshultz !
Ohhh gotcha. Thanks for the detailed explanation! I appreciate it.

I was also thinking if there was some way to implement a op-amp and low pass filter, to clean up some of the high frequency noise, and amplify the signal. Possibly a differential amplifier, one input being the pulse from the arduino, and the second being the voltage at the negative side of the diode? and then the capacitor could theoretically be eliminated, because the arduino is only reading the output of the amplifier instead?

Well wait, now that i think about it, the first input of the differential amplifer would probably have to go somewhere else?

Thanks @sonofcy ! Yeah, I'm not a big fan of them either... I've just been building things with electronics for quite a while, and just starting college, I am getting quite bored in some of my electronics classes :joy:. The first circuit I posted just seems like there's so much room for improvement, so I want to see how much I can get done! I have to submit a finished PCB by the end of this week though, so not much time left to test things.

Thanks again in advance for any input everyone!

Well not quite.
For any given inductor you have to adjust the C to bring the circuit back into resonance. The best balance is when the capacitive reactance and the inductive reactance are the same.

You could also play about with the actual frequency you get the L/C circuit to run at.

Increasing this diameter can improve the range as well.

A good point, because an inductor can develop quite easily over 100V volts signals. I would use a Schottky diode from any input to 5V with another one clamping any negative signal to the ground.
If you are not familiar with these devices see

You can also have a two coil device, one coil for the excitation and another for the reception of the signal. It is also possible to measure not only the amplitude drop of the received signal but also its phase difference. This phase difference is different with different materials and can be used in a crude way to distinguish between different types of metals. But don't get your hopes up too much at differentiation between gold and aluminum.

Just to say that my first job when I left school in 1968 involved working on industrial metal detectors where product, pharmaceuticals and cakes, were sent on a conveyor belt through a tunnel of three coils each consisting of one turn each. The middle coil provided the excitation, and the two outer coils the detection. The two outer coils were wired up so that the signals canceled each other out, and you first got a disturbance in one coil and then the other giving a blip as the product passed through. The two outer coils had to be tuned by hand on every product which involved moving the coils and finally adding small shards of ferrite to balance them.

We were mainly looking for stainless steel, which is hard to detect.

I know that this is not what you are doing with a "conventional" metal detector but a lot of the physics carries over.

Good luck with your project. It sounds like you will have a great future in this industry.

3 Likes

Ohhh I see! That would make sense. We never talked about that, and have only been doing trial and error with the capacitor value.

I think that's what I have been doing? I've been playing around with adjusting the number of pulses at a time, the width of the pulses, and the delay time between the pulses. After sending the pulses there is quite a long delay (over 100µs if I remember correctly), because the Arduino is reading the capacitor voltage and takes that long to do so. I feel like there is a more efficient way of going about doing this... maybe a 555 timer to generate steady pulses, and a differential amplifier to get a reading? I know if I just use a 555 timer for the pulses, the Arduino won't be able to read anything, because it can no longer read how it was with the capacitor!

Thanks again for all the input @Grumpy_Mike !

Edit:
Also, here is my code!

#define I_AM_DEBUGGING//comment to stop serial prints

//#define INVERTED_LOOP//comment to go standard... uncomment for if the signal to the loop is inverted, like when using a driving transistor

#define capPin A5
#define pulsePin A4

#define RED 4
#define GREEN 5
#define BLUE 6


long expectedSum=0; //running sum of 64 sums 
long ignoreCount=0;   //number of ignoreCounted sums
long ignoreAmount=120;//64;//number "ignoreCount" gets to before resetting again... for above threshold
long difference=0;        //difference between sum and avgsum
unsigned long previousTime=0;
unsigned long ledUpdatePeriod=0; 

int numberOfPulses = 10;//number of pulses at one time
int pulseDelay = 1;//microseconds
int pulseSeriesCount = 256;//for loop

int diff;
int multiplier = 1;//general multiplier
int originalMultiplier = 64;//original multiplier... seen as <<6

int noiseThreshold = 150;//threshold for difference seen as 'noise'

int minval=1023;
int maxval=0;
long unsigned int sum=0;

void setup() {
  Serial.begin(9600);

  pinMode(pulsePin, OUTPUT); 
  pinMode(capPin, INPUT);  
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);

  RGBLED(0,0,0);
  for (int i=RED;i<RED+3;i++){
    digitalWrite(i, LOW);
    delay(300);
    digitalWrite(i, HIGH);
  }
  RGBLED(0,0,0);
  delay(1000);
  RGBLED(1,0,0);

  #ifdef INVERTED_LOOP
  digitalWrite(pulsePin, HIGH);
  #else
  digitalWrite(pulsePin, LOW);
  #endif
}

void loop() {
  for (int i=0; i<pulseSeriesCount; i++){
    resetCapacitor();    
    applyPulses();
    readCapacitor();
    LEDLogic();
  }

  int seendifference = totaldifference();

  int myledvalue = map(abs(seendifference)/multiplier,20,1000,0,255);
  
  #ifdef I_AM_DEBUGGING
    Serial.println("sum: "+String(sum)+" - myledvalue: "+String(myledvalue)+" - difference: "+String(difference/multiplier)+" - period: "+String(ledUpdatePeriod));
  #endif
  sum=0;
}

void RGBLED(int R, int G, int B){
  digitalWrite(RED,!R);//drive low to light
  digitalWrite(GREEN,!G);
  digitalWrite(BLUE,!B);
}

void applyPulses(){
    for (int i=0;i<numberOfPulses;i++) {
      #ifndef INVERTED_LOOP
      digitalWrite(pulsePin,HIGH); //take 3.5 uS
      delayMicroseconds(pulseDelay);
      digitalWrite(pulsePin,LOW);  //take 3.5 uS
      delayMicroseconds(pulseDelay);
      #else
      digitalWrite(pulsePin,LOW); //take 3.5 uS
      delayMicroseconds(pulseDelay);
      digitalWrite(pulsePin,HIGH);  //take 3.5 uS
      delayMicroseconds(pulseDelay);
      #endif
    }
}

void resetCapacitor(){
  pinMode(capPin,OUTPUT);
  digitalWrite(capPin,LOW);
  delayMicroseconds(20);
  pinMode(capPin,INPUT);
}

void readCapacitor(){
  //read the charge of capacitor
  int val = analogRead(capPin); //takes 13x8=104 microseconds
  minval = min(val,minval);
  maxval = max(val,maxval);
  sum+=val;//running total
}

void LEDLogic(){
    long unsigned int cTime=millis();
    char buzState=0;
    
    diff = difference/multiplier;
    if (cTime<previousTime+10){
      if (diff>0){
        buzState=1;
      }
      else if(diff<0){
        buzState=2;
      }
    }

    if (cTime>previousTime+ledUpdatePeriod){
      if (diff>0){
        buzState=1;
      }
      else if (diff<0){
        buzState=2;
        previousTime=cTime;
      } 
    }

    if (ledUpdatePeriod>300){
      buzState=0;
    }

    if (buzState==0){
      RGBLED(1,0,0);
    }  

    else if (buzState==1){
      RGBLED(0,1,0);
    }

    else if (buzState==2){
      RGBLED(0,0,1);
    }
}

int totaldifference(){
  //subtract minimum and maximum value to remove spikes
  sum-=minval; 
  sum-=maxval;

  if (expectedSum==0){
    expectedSum=sum*originalMultiplier; //set expectedSum to expected value
  }

  long int avgsum=(expectedSum+100)/originalMultiplier; //originally 32
  difference=sum-avgsum;
  difference=difference*multiplier;

  if (abs(difference)<noiseThreshold*multiplier){
    expectedSum=expectedSum+sum-avgsum;
    ignoreCount=0;
  } 
  else {
    ignoreCount++;
  }
  if (ignoreCount>ignoreAmount){ 
    expectedSum=sum*originalMultiplier;
    ignoreCount=0;
  }

  if (difference==0){
    ledUpdatePeriod=1000000;
  }
  else {
    ledUpdatePeriod=avgsum/(2*abs(difference));
  }  
  
}

usability ideas

  • add a LED parallel to the buzzer so deaf people get feedback.
  • If you use PWM to drive the LED, you might even give feedback about the strength of the signal.



  • Maybe add a discharge path for the capacitor :thinking:

Hey @LarryD !
Wouldn't that discharge the capacitor nonstop? The only way the reading works, is by discharging the capacitor at a set time, after all the pulses have happened, and the ADC reading taken.

Also I should clarify (@robtillaart ), I currently have a RGB LED that I am using, three different colors, plus different flashing frequencies. And I got rid of the buzzer, because it was driving me crazy while testing :sweat_smile: .

  • Discharges all the time as shown, however, you could place a N MOSFET from the resistor to GND, turn on the MOSFET to discharge the capacitor.

IMO the best simple metal detector is still an oscillator like here https://www.allaboutcircuits.com/projects/metal-detector-with-arduino/ and use the arduino to measure the frequency.
The better way would be to use an up and down chirp and measure the response, but that's a different kind of beast (you usually pay good money for it)

1 Like

Got it! :+1:

I was thinking just an oscillator would be better! I was also thinking a differential amplifier could be very useful, but maybe that's incorrect?

Also, I measured some things in lab today... here is an o'scope picture with the first (top) trace being the pulses from the Arduino, and the second (bottom) trace being the voltage at the capacitor!

Thanks again everyone!

  • Notice the capacitor discharging slowly.

I did notice that :thinking: @LarryD . Is that not good? Does that affect the reading?

  • It’s just a comment.
    The Arduino Analog input is > 100M ohms.

  • It would be interesting to see the waveform at the top of the coil.

  • Adding a capacitor (you would need to try different values to get it right) in parallel with the coil to create a resonant circuit would be a good experiment to run. :thinking:
1 Like

So with a 125us analog read time, on that plateau, it gets a 10-bit measure of that 0.864V signal as the result. I wonder what the code is like--is it using the 1.1V analog reference? how tight is the timing of the pulses?

The usual anlog design are 2 oscillators of the same frequency üf ~ 500KHz, one fixed and the other with the detector coil. the difference of both give you a low frequency sine wive in the audible range. You could use the difference + ideal rectifier + C to get a nice signal for the arduino. Downside: you do not know if its magnetic or non-magnetic stuff you are detecting - but you have the same problem with your design. Chirp is still the best - and most complicated :slight_smile:

1 Like

This has a good discussion of different methods:

1 Like

So... some more lab measurements!

5.2µH inductor
Arduino pulse frequency: 134.4kHz

Thanks @LarryD !
As you can see in the above picture, I turned the inductor into an oscillating LC tank circuit!
Trace 3 (green) is the pulses from the Arduino, Trace 1 (Yellow) is the voltage at the capacitor, and Trace 2 (Blue) is the voltage at the top of the inductor.

After measuring the inductance of my coil (5.2µH), and the frequency of the pulses from the Arduino (134.4kHz), I calculated the value capacitor needed and found it to be 270nF. As seen in the picture, the tank circuit is oscillating perfectly!

The on time and off time are determined by the time it takes for the function to actually run, and by a set microsecond delay. Running digitalWrite takes ~3.5µs, and I currently have a delay of 1µs set, so the square wave is 50% duty cycle at 134.4kHz, with a on/off time of 4.5µs.

2 Likes

That's the exact same page I found! That was the most informative website I could find.

1 Like