The Maze/Spring/2019

Controlling a Metal Detecting Sensor

Author/s: Alonso Quintero & Steven Charles Tan
Verification: Steven Charles Tan (Project Manager)
Approval: Steven Charles Tan (Project Manager)

Introduction

The metal detecting sensor is Mainly influenced by Magnetic fields, or interruptions in Magnetic Fields. When the sensor comes in proximity with metal these interruptions in the magnetic field become strong enough to read.

Setting up for a reading

The metal Sensor is composed of an inductor, a diode and a capacitor. In order to take a reading of the Magnetic field the inductor needs to be charged first. This is achieved through a series of pulses. Through many calculations and experiments, 100 pulses with a 20us period and a 50% duty cycle was found to be the optimal charging parameters. this allows the inductor to saturate giving the best resolution. On the falling edge of the last cycle is when the reading should be taken at the capacitor side. This reading will be the magnetic field, and this reading will be the one to drop when there is a disturbance i.e. metal under the inductor.

Procedure for actually reading the sensor

Initially, analogRead commands were used to read the MF readings. This procedure, however, was found to be too slow. It took about 200us to convert a number. By the time a value was done converting, the second sensor could have already been pulsed 10 times or the bot could be veering off. The attempted remedy for this was trying to use a free-running ADC. This, however, came with its own problems as it was trying to read the sensors while they were still being pulsed sometimes so the signals were coming through a little noisy. The final solution was to use a manually controlled ADC that would interrupt the program when a reading was done. By using this method, one sensor could be pulsed while the other is being read. Once the reading is done the ADC conversion bit is turned off. This method provided the most constant signal allowing for a finely tuned threshold. Once the readings are taken, the readings are added to a running average. a size of about 32 readings were used for the average. This way there is a bit of a history on what the reading have been and an outlier will not disturb the outcome too much.


 
/*
* Interrupt controlled ADC
* starts a conversion
* does other stuff
* and is interrupted when values are ready
* 
* Alonso Quintero 05/01/19
*/

//Sensor Variables
int npulse=100;
const int8_t inttime=10;
int8_t senstate=0;
int8_t sen[2]; //pin Register values
int8_t caps[2]; //input pins
int8_t pulses[2]; //output pins
int8_t pin_cap = A1;
int8_t pin_cap2 = A3; //3DoT
int8_t pin_pulse = A0;
int8_t pin_pulse2 = A2;
int diff; //difference between threhold and the average
int16_t threshold;
int16_t threshold2;
bool inter = false; // Boolean variable to indicate when the robot is in an intersection

//Running Average Variables
uint16_t total=0;
uint16_t total2=0;
const int samples=64;
uint16_t readings[samples];
uint16_t readings2[samples];
int8_t readIndex = 0;
int average = 0; // the average
int average2 = 0;



void initsen(){
caps[0] = pin_cap;
caps[1] = pin_cap2;
pulses[0] = pin_pulse;
pulses[1] = pin_pulse2;
sen[0]=B00000110; //ADC5
sen[1]=B00000100; //ADC3
pinMode(pin_pulse, OUTPUT); 
pinMode(pin_cap, INPUT); 
pinMode(pin_pulse2, OUTPUT);
pinMode(pin_cap2, INPUT);
for (int i = 0; i < samples; i++) {
readings[i] = 0;
readings2[i]=0;
}
}

void initADC(){
ADMUX &= B11001111; //clear ADLAR & ADCL
ADMUX |= B11000000; //set reference
ADMUX &= B11100000; //clear MUX
ADMUX |= B00000110; //set MUX
ADCSRA |= B10000000; //ADEN
ADCSRA |= B00000111; //prescalar
ADCSRA |= B00001000; //ADC Interrupt enable
sei(); //Global Interrupt
readFlag = 0; 
}
// Initialization
void setup(){
initsen();
initADC();
Serial.begin(9600);
readthres(); //set the threshold
}

// Interrupt service routine for the ADC completion
ISR(ADC_vect){

// Must read low first
analogVal = ADCL | (ADCH << 8);
runningaverage(senstate);
ADCSRA &= B10111111; // Clear ADSC
}
void pulsesen(){
/*Pulses Sensor 1 first for 50% of the pulses
* then it pulses the second sensor and the first at the same
* time and then takes the reading of the first sensor.
* then it pulses the second sensor. and reads second sensor at the end.
*/
for (int ipulse = 0; ipulse < npulse*1.5; ipulse++) {
if(ipulse=npulse*0.5 && ipulse<npulse){ digitalwrite(pulses[0],high);="" digitalwrite(pulses[1],low);="" delaymicroseconds(inttime);="" digitalwrite(pulses[0],low);="" digitalwrite(pulses[1],high);="" }else="" if(ipulse="=npulse){" admux="" &="B11100000;" clears="" the="" pins="" |="sen[0];" sets="" pin="" adcsra="" starts="" conversion="" delaymicroseconds(40);="" pinmode(caps[0],output);="" digitalwrite(caps[0],low);="" delaymicroseconds(20);="" pinmode(caps[0],input);="">npulse){
digitalWrite(pulses[1],HIGH); //takes 3.5 microseconds
delayMicroseconds(inttime);
digitalWrite(pulses[1],LOW);
delayMicroseconds(inttime);
}
}
ADMUX &= B11100000; // clears the pins
ADMUX |= sen[1]; // sets the pin
ADCSRA |= B01000000; // starts the conversion
delayMicroseconds(40);
pinMode(caps[1],OUTPUT);
digitalWrite(caps[1],LOW);
delayMicroseconds(20);
pinMode(caps[1],INPUT);
}
int runningaverage(uint16_t whichsen){
if (whichsen == 0){
total -= readings[readIndex];
// read from the sensor:
readings[readIndex] = analogVal;
// add the reading to the total:
total += readings[readIndex];
// calculate the average:
average = total / samples;
readIndex++;
senstate = 1;
} else if(whichsen==1){
total2 -= readings2[readIndex];
readings2[readIndex] = analogVal;
total2 += readings2[readIndex];
average2 = total2 / samples;
senstate = 0;
}
// if we're at the end of the array...
if (readIndex >= samples) {
// ...wrap around to the beginning:
readIndex = 0;
}
}
void readthres(){
for (int a = 0; a<101;a++){
pulsesen();
delay(10);
}
//Serial.print("Reading Threshold");
threshold = average-3;
threshold2 = average2-3;
Serial.println();
Serial.print(threshold);
Serial.println();
Serial.print(threshold2);
}
</npulse){>

Some issues that were encountered

The sensors now are configured such that the sensors are being pulsed almost at the same time. one sensor is pulsed for half the total sample duration, if the number of pulses is 100 then the first sensor is pulsed for 50 pulses first, then both sensors are pulsed at opposite intervals for another half. Finally, the second sensor is pulsed for the remaining half while the first sensor is being read, the second sensor will be read at the end of its pulsing. It was configured this way due to the following issues found when both sensors were pulsed at the same time.

  1. The voltage dropped for both sensors as they were pulling too much current for the controller to be able to maintain a steady voltage. this meant a loss in resolution and an inaccuracy in the readings.
  2. The first part was remedied by alternating the pulsing such that they were only staggered by half a period (10us). This, however, came with its own issue of once the sensors were done, the ADC could only read one sensor at a time. One sensor, therefore, would be discharging for the 200us it takes the ADC to convert the first reading. This lead to one sensor showing a reading lower than the first sensor, Which was not too bad except for the fact that the robot was now slower at responding on the second sensor’s side. This led to the robot veering off course or not updating the second readings fast enough and getting confused thinking it was at an intersection.

The second issue was remedied by implementing the current procedure. as shown in the figure below.

Conclusion

This Sensor is able to detect a thin line of copper wire fairly accurately. It detects the copper well enough to follow it and stay on course. The sensors were not able to be run simultaneously but rather staggered enough to complete at least one reading before the next sensor is ready to be read.