NZDoug
Published © CC BY

Pocket Metal Locator

Cool little Pocket Metal Locator sensitive enough to identify small nails and tacks in wood with four independent search coils and LEDs.

AdvancedFull instructions provided3,471
Pocket Metal Locator

Things used in this project

Hardware components

Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Thingiverse

This cool little Pocket Metal Locator is sensitive enough to identify small nails and tacks in wood and compact enough to fit into awkward spaces making it convenient to carry and use for metal locating. The unit has four independent search coils and color LED indicators making it easy to cover a larger search area quickly while being able to accurately identify the target. This neat little device is self-calibrating with one button operation, rechargeable through a USB port and uses color LEDs, sound and vibration to indicate target strength. Included in the instructable is all of the designs, testing, code and 3D files required to build on your own. I hope you enjoy building and using this as much as I have!!

Schematics

Pocket Metal Locator

This cool little Pocket Metal Locator is sensitive enough to identify small nails and tacks in wood and compact enough to fit into awkward spaces making it convenient to carry and use for metal locating.

The unit has four independent search coils and color LED indicators making it easy to cover a larger search area quickly while being able to accurately identify the target.

Code

Pocket_Metal_Locator_V11.ino

C/C++
This cool little Pocket Metal Locator is sensitive enough to identify small nails and tacks in wood and compact enough to fit into awkward spaces making it convenient to carry and use for metal locating.

The unit has four independent search coils and color LED indicators making it easy to cover a larger search area quickly while being able to accurately identify the target.

This neat little device is self-calibrating with one button operation, rechargeable through a USB port and uses color LEDs, sound and vibration to indicate target strength.

Included in the instructable is all of the designs, testing, code and 3D files required to build on your own. I hope you enjoy building and using this as much as I have!!
/* Pocket Metal Locator - Arduino Pro Mini April 2018 TechKiwiGradgets
Version 11 - First Instructable Release

*/

// WS2182 LED Driver Library Setup
#include "FastLED.h"
// How many leds in your strip?
#define NUM_LEDS 4 // Note: First LED is address 0
#define DATA_PIN 11 // Note: D11 used to control LED chain

// Define the array of leds
CRGB leds[NUM_LEDS];

// Smoothing Variables for LPF
const float alpha = 0.85; 
float smoothedvalue1 = 1000;
float smoothedvalue2 = 1000;
float smoothedvalue3 = 1000;
float smoothedvalue4 = 1000;

int sthreshold = 100; // Minimum reading from pulseIn function ensure sampling above noise

float ledthreshold1 = 0.93; // Percentage below baseline before setting LEDs Purple
float ledthreshold2 = 0.96; // Percentage below baseline before setting LEDs Red
float ledthreshold3 = 0.99; // Percentage below baseline before setting LEDs Green
float ledthreshold4 = 0.998; // Percentage below baseline before setting LEDs Blue



// Callibration Variables

boolean sample = false; // if true then do not recallibrate thresholds
int calcounter = 0; // Used to count the number of times a sample has been taken
int scount = 200; // Number of reads before calibrating once only after power up
int numsamples = 20; // Number of samples taken before averaging them
int t1 = 100; // Duration in microseconds that LEDs on during callibration



long calav1 = 0; // Used to calcuate average of ten samples
long calav2 = 0; // Used to calcuate average of ten samples
long calav3 = 0; // Used to calcuate average of ten samples
long calav4 = 0; // Used to calcuate average of ten samples

int div1 = 0;// Divisor counter for number of samples taken
int div2 = 0;
int div3 = 0;
int div4 = 0;

// Four Channels denoted by A,B,C,D

int divA = 0;
long tempA = 0;
long pcounterA = 0; // Unfiltered Pulse Width of Channel 1

int divB = 0;
long tempB = 0;
long pcounterB = 0; // Unfiltered Pulse Width of Channel 2

int divC = 0;
long tempC = 0;
long pcounterC = 0; // Unfiltered Pulse Width of Channel 1

int divD = 0;
long tempD = 0;
long pcounterD = 0; // Unfiltered Pulse Width of Channel 1

int pwindow = 7500; // Maximum timeout value for pulsewidth

// ****************************************************
int dly1 = 4; // Period of time that TXR pin stays high (typicLLY 4uS)
int dly2 = 1; // Delay after TXR pin goes LOW before reading starting to read the pulse duration - typically 1 uS 
int dly3 = 500; // Delay in Microseconds after sampling pulsewidth  before starting next cycle

// ****************************************************


int threshdiff = 15; // Add to average to become specific threshold where LED will light (Changed V21 from 25 to 20)

long pulseav = 0; // Stores value of output for calibration

// Vibration Motor 
  boolean haptic = false; // Flag used to turn on vibration motor for a period of time
  int vduration = 1; // Number of program cycles that vibration motor enabled 
  int vcounter = vduration; // v cycle counter  


void setup() {
// Vibration Motor/Haptic Feedback
  pinMode(12,OUTPUT); // Vibration Motor driver from Arduino           - D10
  digitalWrite(12,LOW); // Turn off motor

  
  
  Serial.begin(115200); // Setupserial interface for test data outputs


// WS2182 LED Driver Setup
  LEDS.addLeds<WS2812,DATA_PIN,GRB>(leds,NUM_LEDS); // Default if RGB for this however may vary dependent on LED manufacturer
  LEDS.setBrightness(5); //Set brightness of LEDs here
  // limit my draw to 1A at 5v of power draw
  FastLED.setMaxPowerInVoltsAndMilliamps(5,100);

  FastLED.setDither(0); // Turns off Auto Dithering function to remove flicker


// Arduino Mini Pro logical pin assignements
//  Logical Pin Name 2,3,4,5,6,7,8,9 Equates to D2-D9 Pin Name Printed On Board 
//  Logical Pin Name 10,11,12,13,14,15,16,17 Equates to D10-D13, A0-A3 Pin Name Printed On Board

// Piezo Buzzer
  pinMode(10,OUTPUT); // Piezo Buzzer driver from Arduino           - D10




// Transmit
  pinMode(2,OUTPUT); // Pulse output from Arduino                   - D2
  pinMode(4,OUTPUT); // Pulse output from Arduino                   - D4
  pinMode(6,OUTPUT); // Pulse output from Arduino                   - D6
  pinMode(8,OUTPUT); // Pulse output from Arduino                   - D8
 
// Channel A
  pinMode(3,INPUT); // Signal input to Arduino from pin 2 on LM339  - D3

// Channel B
  pinMode(5,INPUT); // Signal input to Arduino from pin 1 on LM339  - D5

// Channel C
  pinMode(7,INPUT); // Signal input to Arduino from pin 14 on LM339 - D7

// Channel D
  pinMode(9,INPUT); // Signal input to Arduino from pin 13 on LM339 - D9
    
  }

void loop(){

// Pulse and read Coil  ---------------------------------------------


// Channel 1 (point of Wand)

digitalWrite(2,HIGH); // Set the TX pin to high
delayMicroseconds(dly1); // Delay before setting low on output pin
digitalWrite(2,LOW); // Set TX pin to low
delayMicroseconds(dly2); // Delay before sampling pulse width
pcounterA = pulseIn(3,LOW,pwindow);
digitalWrite(2,LOW); // Set the TX pin to LOW
delayMicroseconds(dly3); // Delay before sampling pulse width


// Apply Low Pass Filter to signal to smooth Channel 1
  if (pcounterA >= sthreshold) {
    smoothedvalue1 = (alpha * smoothedvalue1) + ( (1 - alpha) * pcounterA);
    }
  pcounterA = smoothedvalue1;


// Channel 2

digitalWrite(4,HIGH); // Set the TX pin to high
delayMicroseconds(dly1); // Delay before setting low on output pin
digitalWrite(4,LOW); // Set TX pin to low
delayMicroseconds(dly2); // Delay before sampling pulse width
pcounterB = pulseIn(5,LOW,pwindow);
digitalWrite(4,LOW); // Set the TX pin to LOW
delayMicroseconds(dly3); // Delay before sampling pulse width

// Apply Low Pass Filter to signal to smooth Channel 2
  if (pcounterB >= sthreshold) {
    smoothedvalue2 = (alpha * smoothedvalue2) + ( (1 - alpha) * pcounterB);
    }
  pcounterB = smoothedvalue2;

// Channel 3

digitalWrite(6,HIGH); // Set the TX pin to high
delayMicroseconds(dly1); // Delay before setting low on output pin
digitalWrite(6,LOW); // Set TX pin to low
delayMicroseconds(dly2); // Delay before sampling pulse width
pcounterC = pulseIn(7,LOW,pwindow);
digitalWrite(6,LOW); // Set the TX pin to LOW
//delayMicroseconds(dly3); // Delay before sampling pulse width

// Apply Low Pass Filter to signal to smooth Channel 3
  if (pcounterC >= sthreshold) {
    smoothedvalue3 = (alpha * smoothedvalue3) + ( (1 - alpha) * pcounterC);
    }
  pcounterC = smoothedvalue3;

// Channel 4 

digitalWrite(8,HIGH); // Set the TX pin to high
delayMicroseconds(dly1); // Delay before setting low on output pin
digitalWrite(8,LOW); // Set TX pin to low
delayMicroseconds(dly2); // Delay before sampling pulse width
pcounterD = pulseIn(9,LOW,pwindow);
digitalWrite(8,LOW); // Set the TX pin to LOW
delayMicroseconds(dly3); // Delay before sampling pulse width

// Apply Low Pass Filter to signal to smooth Channel 4
  if (pcounterD >= sthreshold) {
    smoothedvalue4 = (alpha * smoothedvalue4) + ( (1 - alpha) * pcounterD);
    }
  pcounterD = smoothedvalue4;



// Print value then Reset the counter

/*

  Serial.print(pcounterA);
  Serial.print("  ");  
  Serial.print(calav1);
  Serial.print(" ");
  Serial.print(pcounterB);
  Serial.print("  ");  
  Serial.print(calav2);
  Serial.print(" ");
  Serial.print(pcounterC);
  Serial.print("  ");  
  Serial.print(calav3);
  Serial.print(" ");
  Serial.print(pcounterD);
  Serial.print("  ");  
  Serial.println(calav4);

*/


// Callibation of Thresholds on Powerup
  // Wait for a period of time to set baseline for each coil
      
   if (sample == false){
         calcounter++;
         }

     if ( calcounter > (scount-numsamples) ) { // Wait for 90 then add up the samples to prepare to calculate the average

       if (pcounterA > sthreshold) {
         calav1 = calav1 + pcounterA;
         div1++;
       }
  
       if (pcounterB > sthreshold) {
         calav2 = calav2 + pcounterB;
         div2++;
       }
    
       if (pcounterC > sthreshold) {
         calav3 = calav3 + pcounterC;
         div3++;
       }
    
       if (pcounterD > sthreshold) {
         calav4 = calav4 + pcounterD;
         div4++;
       }
  
    }

  if ((calcounter > scount)&&(sample == false)){

    // Set the thresholds

    calav1 = calav1/div1;
    calav2 = calav2/div2;
    calav3 = calav3/div3;
    calav4 = calav4/div4;      
    
  // Flash LED to show calibrate done

  // 0-3 Blue
    leds[3] = CRGB::Blue;
    FastLED.show();
    delay(t1);  
    leds[3] = CRGB::Black;
 
    leds[2] = CRGB::Blue;
    FastLED.show();
    delay(t1);  
    leds[2] = CRGB::Black;

     leds[1] = CRGB::Blue;
    FastLED.show();
    delay(t1);  
    leds[1] = CRGB::Black;           

     leds[0] = CRGB::Blue;
    FastLED.show();
    delay(t1);  
    leds[0] = CRGB::Black;

  // 3-0 Green
    leds[3] = CRGB::Green;
    FastLED.show();
    delay(t1);  
    leds[3] = CRGB::Black;
 
    leds[2] = CRGB::Green;
    FastLED.show();
    delay(t1);  
    leds[2] = CRGB::Black;

     leds[1] = CRGB::Green;
    FastLED.show();
    delay(t1);  
    leds[1] = CRGB::Black;           

     leds[0] = CRGB::Green;
    FastLED.show();
    delay(t1);  
    leds[0] = CRGB::Black;

    FastLED.show();

// Sound Buzzer

      // digitalWrite(10,HIGH); // Set the output pin to high to activate the LED
//      delay(t1*2);
      digitalWrite(10,LOW); // Set the output pin to high to activate the LED   
    
    // Reset the sample flag
    sample = true;
    calcounter = 0;
  }

  if (sample == true) {
   updateLEDs();  
  }


// Haptic Feedback - If threshold exceeded turn on vibration motor for "vduration" cycels

  if(haptic == true) { // If flag set then turn on motor
    digitalWrite(12,HIGH); // Set the output pin to high to activate the Vibrate Motor
  
    if (vcounter >= 1){ 
    vcounter--; // decrement counter
    }else{
        digitalWrite(12,LOW); // Set the output pin to LOW to deactivate the Vibrate Motor
        haptic = false; // Reset vibration flag after number of cycles 
        vcounter = vduration; // Reset vibration counter
    }
  
  }
  
}


// Subroutines

void updateLEDs() {

// Display results
// Purple - Strongest target signal + (Beep Piezo and Haptic Feedback)
// Red - High +(Haptic Feedback)
// Green - Moderate +(Haptic Feedback)
// Blue - Weakest target signal



// Turn off all LEDs
leds[0] = CRGB::Black;
leds[1] = CRGB::Black;
leds[2] = CRGB::Black;
leds[3] = CRGB::Black;

digitalWrite(10,LOW); // Set the output pin to LOW to deactivate the Piezo Speaker
digitalWrite(12,LOW); // Set the output pin to LOW to deactivate Vibrating Motor


// *************** Channel 1  


if (pcounterA < (calav1*(ledthreshold1))) { // Display Purple if strong target

    leds[3] = CRGB::Purple;
     digitalWrite(10,HIGH); // Set the output pin to high to activate the Piezo Speaker
     haptic = true;
  } else

if (pcounterA < (calav1*(ledthreshold2))) { // Display Blue for moderate strength target

    leds[3] = CRGB::Red;
     haptic = true;
  } else

if (pcounterA < (calav1*(ledthreshold3))) { // Display Blue for moderate strength target

    leds[3] = CRGB::Green;
     haptic = true;
  } else

if (pcounterA < (calav1*(ledthreshold4))) { // Add additional percentage point to threshold due to sensitivity of channel 1

    leds[3] = CRGB::Blue;

  }  

// Channel 2 Display  


if ((pcounterB < calav2*ledthreshold1 )) { // Display Green if strong target

    leds[2] = CRGB::Purple;
     digitalWrite(10,HIGH); // Set the output pin to high to activate the Piezo Speaker
     haptic = true;
    
  } else

if ((pcounterB < calav2*ledthreshold2 )) { // Display Blue for moderate strength target

    leds[2] = CRGB::Red;
     haptic = true;
  }  else

if ((pcounterB < calav2*ledthreshold3 )) { // Display Blue for moderate strength target

    leds[2] = CRGB::Green;
     haptic = true;
  } else

if ((pcounterB < calav2*ledthreshold4 )) { // Display Blue for moderate strength target

    leds[2] = CRGB::Blue;

  } 

// Channel 3 Display  


if ((pcounterC < calav3*ledthreshold1 )) { // Display Green if strong target

    leds[1] = CRGB::Purple;
     digitalWrite(10,HIGH); // Set the output pin to high to activate the Piezo Speaker
     haptic = true;
    
  } else

if ((pcounterC < calav3*ledthreshold2 )) { // Display Blue for moderate strength target

    leds[1] = CRGB::Red;
     haptic = true;
  }  else

if ((pcounterC < calav3*ledthreshold3 )) { // Display Blue for moderate strength target

    leds[1] = CRGB::Green;
     haptic = true;
  } else

if ((pcounterC < calav3*ledthreshold4 )) { // Display Blue for moderate strength target

    leds[1] = CRGB::Blue;

  }

// Channel 4 Display 


if ((pcounterD < calav4*ledthreshold1 )) { // Display Green if strong target

    leds[0] = CRGB::Purple;
     digitalWrite(10,HIGH); // Set the output pin to high to activate the Piezo Speaker
     haptic = true;
    
  } else

if ((pcounterD < calav4*ledthreshold2 )) { // Display Blue for moderate strength target

    leds[0] = CRGB::Red;
    haptic = true;
  } else

if ((pcounterD < calav4*ledthreshold3 )) { // Display Blue for moderate strength target

    leds[0] = CRGB::Green;
    haptic = true;
  } else

if ((pcounterD < calav4*ledthreshold4 )) { // Display Blue for moderate strength target

    leds[0] = CRGB::Blue;

  } 



    FastLED.show();
}

Credits

NZDoug

NZDoug

0 projects • 15 followers

Comments