Test Run
I assembled the board, made a simple coil and powered up the Nano without the coil power supply. Some edits to the code but otherwise it appears to be working.
Connected the Coil power supply and amazingly it worked. Some tweaks to the exponential filter constants and it detects small coins within half a coil diameter and large coins at about one coil diameter. About as good as similar (honest) detectors on the Internet.
On the Internet I have seen a demonstration of a PIMD discriminating (not responding to) aluminum foil. This is not real, the detector is just rejecting strong and good conducting targets. Easy to do but does not make a good metal detector.
Hers is the setup:
And the Code:
/* Arduino Pulse Induction Metal Detector */
// Pin definitions
#define pulsePin 4 // For coil MOSFET gates
#define beepPin 5 // Buzzer pin
#define compPlus 6 // Plus (signal) comparator input pin
#define compMinus 7 // Minus (level) comparator input pin
#define ledPin0 A0 // Log bar LED0
#define ledPin1 A1 // Log bar LED1
#define ledPin2 A2 // Log bar LED2
#define ledPin3 A3 // Log bar LED3
#define ledPin4 A4 // Log bar LED4
#define ledPin5 A5 // Log bar LED5
// Fast versions of digitalWrite(Pin,HIGH); and digitalWrite(Pin,LOW); for all pins on UNO/Nano
#define fastHighPin(Pin) if ((Pin)<8) PORTD|=1<<(Pin); else if ((Pin)<14) PORTB|=1<<(Pin)-8; else if ((Pin)<20) PORTC|=1<<(Pin)-14;
#define fastLowPin(Pin) if ((Pin)<8) PORTD&=~(1<<(Pin)); else if ((Pin)<14) PORTB&=~(1<<(Pin)-8); else if ((Pin)<20) PORTC&=~(1<<(Pin)-14);
volatile unsigned int Magic0=0;
volatile unsigned int Phase0=0;
ISR(TIMER0_COMPB_vect) {
Phase0+=Magic0;
if (Phase0<0x8000) {
fastLowPin(beepPin);
} else {
fastHighPin(beepPin);
}
}
void setup() {
// Set up pulse pin first - Note: Electronics logic is inverted
digitalWrite(pulsePin,HIGH);
pinMode(pulsePin,OUTPUT);
// Set up comparator inuts
pinMode(compPlus,INPUT);
pinMode(compMinus,INPUT);
// Set up Beeper
pinMode(beepPin,OUTPUT);
digitalWrite(beepPin,LOW);
// Set up Config LED
pinMode(LED_BUILTIN,OUTPUT);
// Set up LED bar display
pinMode(ledPin0,OUTPUT);
pinMode(ledPin1,OUTPUT);
pinMode(ledPin2,OUTPUT);
pinMode(ledPin3,OUTPUT);
pinMode(ledPin4,OUTPUT);
pinMode(ledPin5,OUTPUT);
// Clear LEDs
digitalWrite(LED_BUILTIN,LOW);
digitalWrite(ledPin0,LOW);
digitalWrite(ledPin1,LOW);
digitalWrite(ledPin2,LOW);
digitalWrite(ledPin3,LOW);
digitalWrite(ledPin4,LOW);
digitalWrite(ledPin5,LOW);
// No interrupts (just read the ACO bit)
ACSR=0x00;
// Reset timer 1
TIMSK1=0x00; // Disable all timer interrupts
TCCR1A=0x00; // Normal (just count up and roll over) Config
TCCR1B=0x00; // Stop timer
// Piggy back tone to timer0
OCR0B=0x80; // Trigger interrupt away from millis() interrupt
TIMSK0|=(1<<OCIE0B); // Enable Timer0 Compare B interrupt
// Set Serial - for debug
// delay(100);
// Serial.begin(9600);
// while (!Serial);
// delay(100);
}
void loop() {
// PIMD constants
// Repetition rate
unsigned long previousMillis=0;
unsigned long currentMillis;
unsigned long intervalMillis=20;
// Coil pulse timing
unsigned long pulseTime=500; // Coil on time (us)
unsigned long startMicros;
unsigned long endMicros;
// Signal processing
unsigned long decayTime; // The Signal
unsigned long refExpFilter=0; // Reference exponential filter
unsigned long sigExpFilter=0; // Signal exponential filter
unsigned long refTest; // Reference (smoothed) test value
unsigned long sigTest; // Signal (smoothed) test value
unsigned int refFilterConst=9; // Exponential filter time power constant (setting time about 60 seconds)
unsigned int sigFilterConst=3; // Exponential filter response time: lower is faster but more noisy
unsigned int thresholdConst=1; // Sensitivity
// Beeper frequency
unsigned int magicNum=0; // magicNum=Freq*67.108864;
// Stop tone and set beepPin LOW
Magic0=0;
Phase0=0;
// Clear configuration
bool configState=true;
unsigned int Config=0; // Config flag {0|1-6}
// Get configuration before start
bool Start=false; // Start flag {false|true}
// Clear Config and bar display LEDs
digitalWrite(LED_BUILTIN,LOW);
digitalWrite(ledPin0,LOW);
digitalWrite(ledPin1,LOW);
digitalWrite(ledPin2,LOW);
digitalWrite(ledPin3,LOW);
digitalWrite(ledPin4,LOW);
digitalWrite(ledPin5,LOW);
// Get configuration
while (configState) {
// Set Config (A7) before turning on power
while (analogRead(A7)<=511) { // While button pushed
Config=Config%6;
Config++;
digitalWrite(ledPin0,LOW);
digitalWrite(ledPin1,LOW);
digitalWrite(ledPin2,LOW);
digitalWrite(ledPin3,LOW);
digitalWrite(ledPin4,LOW);
digitalWrite(ledPin5,LOW);
if (Config==1) {
magicNum=16384; // 244Hz (freq*67.108864)
refFilterConst=9; // Setting time about 60 seconds
sigFilterConst=3; // Response time (medium)
thresholdConst=1; // Sensitivity (high)
digitalWrite(ledPin0,HIGH);
}
if (Config==2) {
magicNum=0; // 0Hz (off)
refFilterConst=9; // Setting time about 60 seconds
sigFilterConst=3; // Response time (medium)
thresholdConst=1; // Sensitivity (high)
digitalWrite(ledPin1,HIGH);
}
if (Config==3) {
magicNum=16384; // 244 Hz;
refFilterConst=8; // Setting time about 30 seconds
sigFilterConst=3; // Response time (medium)
thresholdConst=1; // Sensitivity (high)
digitalWrite(ledPin2,HIGH);
}
if (Config==4) {
magicNum=16384; // 244 Hz;
refFilterConst=7; // Setting time about 15 seconds
sigFilterConst=2; // Response time (fast)
thresholdConst=1; // Sensitivity (high)
digitalWrite(ledPin3,HIGH);
}
if (Config==5) {
magicNum=0; // 0Hz (off)
refFilterConst=9; // Setting time about 60 seconds
sigFilterConst=3; // Response time (medium)
thresholdConst=1; // Sensitivity (high)
digitalWrite(ledPin4,HIGH);
}
if (Config==6) {
magicNum=0; // 0Hz (off)
refFilterConst=9; // Setting time about 60 seconds
sigFilterConst=3; // Response time (medium)
thresholdConst=1; // Sensitivity (high)
digitalWrite(ledPin5,HIGH);
}
delay(500);
}
// Use power on to exit config
while (analogRead(A6)<=511) { // If exit config state
while (analogRead(A6)<=511); // Wait for buton release
delay(20); // Wait for debounce
configState=false; // Set exit config state
}
}
Start=true;
while (Start) {
// Set up 20ms cycle
currentMillis=millis();
if (currentMillis-previousMillis>=intervalMillis) {
previousMillis=currentMillis;
// Pulse the coil - Note: Electronics logic is inverted
fastLowPin(pulsePin);
delayMicroseconds(pulseTime);
fastHighPin(pulsePin);
// Measure coil decay time (after coil turned off)
TCCR1B=0x01; // Start timer 1 (16 MHz clock)
while (~ACSR&0x20); // Wait for comparator to trip
TCCR1B=0x00; // Stop timer 1
decayTime=TCNT1; // Read timer 1
TCNT1=0; // Reset timer 1
// Update reference exponential filter
refExpFilter=refExpFilter-(refExpFilter>>refFilterConst)+decayTime;
// Update signal exponential filter
sigExpFilter=sigExpFilter-(sigExpFilter>>sigFilterConst)+decayTime;
// Set LED and tone on if signal detected
refTest=(refExpFilter>>refFilterConst);
sigTest=(sigExpFilter>>sigFilterConst);
if (sigTest>refTest) {
sigTest=(sigTest-refTest)>>thresholdConst;
} else {
sigTest=0; // No signal "reset"
}
// Turn off display
digitalWrite(ledPin0,LOW);
digitalWrite(ledPin1,LOW);
digitalWrite(ledPin2,LOW);
digitalWrite(ledPin3,LOW);
digitalWrite(ledPin4,LOW);
digitalWrite(ledPin5,LOW);
if (sigTest>0) {
// Generate log bar display
digitalWrite(ledPin0,HIGH);
if (sigTest>>=1) digitalWrite(ledPin1,HIGH);
if (sigTest>>=1) digitalWrite(ledPin2,HIGH);
if (sigTest>>=1) digitalWrite(ledPin3,HIGH);
if (sigTest>>=1) digitalWrite(ledPin4,HIGH);
if (sigTest>>=1) digitalWrite(ledPin5,HIGH);
// Start tone
Magic0=magicNum;
} else {
// Stop tone and set beepPin LOW
Magic0=0;
Phase0=0;
}
}
// Check for power down and restart loop();
if (analogRead(A6)<=511) {
while (analogRead(A6)<=511); // Wait for buton release
delay(20); // Wait for debounce
Start=false; // Set flag off
}
}
}
Receive Signals
Here is the no target receive signal:
It approximates the simulation with the exception of the diodes 1.6v rather than 0.9v.
The signal falls to the threshold voltage (i.e. 40mV) after about 14 us.
Here is a small target response:
The signal falls to the threshold voltage after about 20 us.
And here is a (very) large target response:
The pulse cycle will wait until the signal decays to the threshold, but after 8 ms the results will be meaningless.
Improvements
The first improvement was to lower the threshold voltage to 30mV to increase the sensitivity to small targets. The optimal threshold voltage will depend on the picked up noise level and usability. Its the old problem of detecting a uV signal on top of mV noise.
The main problem that I see is that the exponential smoothing/filtering works but would be better before the analog comparator. Perhaps a higher order low pass filter to remove as much of the no target signal as possible before the analog comparator and the exponential filter post the analog comparator.
AlanX
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.