Импульсный металлодетектор на Arduino. Изобретаем велосипед.

Сначала просто собирался сделать эксперимент и посмотреть, будет ли работать и хватит ли быстродействия, но получилось вполне приличное для моего скудного опыта устройство. За основу была взята схема импульсника Tracker-PI, выброшены мозги, программа для Arduino своя, схему рисовал в Proteus, датчик сделан планарный (по этой схеме исчточник здесь). Все остальные файлы смотрите в конце статьи.

Характеристики получились следующими (тестировал на том что было):

  • 10 коп. СССР – 12-15 см
  • 2 коп.  царские (2 см диаметр, медь) – 17-22 см
  • Патрон немецкий первой мировой – 25-30 см
  • Консервная банка (как от сгущенки) – 45 см
  • Старый CD-привод – 60-70 см.

Схема:

Кроме выкинутого контроллера, схема осталась без изменений, за исключением делителя для измерения напряжения. Измеряет, кстати, не совсем точно, видимо связано с настройкой АЦП Arduino на максимальную частоту. С настройкой проблем особых не было, вместо R25 сначала поставил подстроечник что и вам советую, им выровнял полочку на осциллограмме (осциллограммы сброшу позже, разбирать лениво), измерил сопротивление подстроечного – оказалось ровно 400 ом, поставил с таким же номиналом обычный резистор – ИМХО надежнее. По невнимательности пару раз спалил микросхемы и стабилизатор 5V. Включать устройство без катушки не стоит – из R7 пойдет дымок. R1 поставил 0.25Вт – при частоте импульсов 140-160 Гц не греется (если греется – значит либо частота слишком высока, либо импульс шире, чем должен быть). Так же не стоит подключать металлоискатель к импульсному источнику питания – работать не будет (да ещё и микросхемы погорят).

В качестве дисплея использовался китайский 1602 с кнопками. Если такого же нет, функцию опроса кнопок нужно будет переделать (функция должна возвращать такие же значения как в скетче). Кнопок нужно всего три. Вот, кстати, и сам скетч:

#include <LiquidCrystal.h>

//(c) Tsaryk Aliaksei - rst10h - 2014

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
#define FASTADC 1
 
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
 
//для пищалки
//#define R3_PIN       A0
#define SPEAKER_PIN  11
#define T2_WGM   0b010
#define T2_COMA  0b01
#define T2_CS    0b111
 
// параметры подстройки
int pMID=0;
int pMAX=0;
int pMIN=0;
int Sensv = 0; //(0 - макс чувствительность, минимум - около сотни наверное.)
int LastData[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int SetupData[200];
void delayms(int ms) {
  for (int i=0;i<ms;i++) delayMicroseconds(1000);
}
void setup() {
  //set port/pin  mode
  //DDRD = 0xFF;//all outputs
  //DDRC = 0x0F;//all inputs
  //DDRD = 0xFF;//all outputs
  //DDRC = B11111110;
  //TIMER INTERRUPT SETUP
  
  DDRD = DDRD | B00001110;
  PORTD = B00001110;
 
  //PORTD = 0;
  
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print("START  ");
  
  #if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif
 
  //для динамика
 
  TCCR2A = (T2_COMA << 6) | (T2_WGM & 0b011);
  TCCR2B = ((T2_WGM & 0b100) << 1) | T2_CS;
  OCR2A = 18;
  // pinMode(R3_PIN, INPUT);
 // pinMode(SPEAKER_PIN, INPUT);  
 // pinMode(SPEAKER_PIN, OUTPUT);
  noInterrupts();
  beep(100);
  delayms(100);
  beep(100);
  delayms(100);
  beep(100);
  delayms(1000); 
  //посылаем пару сотен импульсов в холостую просто так 
  //
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("SETUP...  ");
  //while (1) 
  for (int i=0;i<200;i++) {  
  int t = SendPulse();  
  delayMicroseconds(6500); 
  }
  //посылаем 200 импульсов для настройки
  for (int i=0;i<200;i++) {
  int t = SendPulse();
  SetupData[i]=t;
  delayMicroseconds(6500);   
  }
  int tmin=1024;
  int tmax=0;
  unsigned long tnak=0;
  for (int i=0;i<200;i++) {
    tnak=tnak+SetupData[i];
    if (SetupData[i]>tmax) tmax = SetupData[i];
    if (SetupData[i]<tmin) tmin = SetupData[i];
  }  
 pMID = tnak/200;
 pMIN = tmin;
 pMAX = tmax;
 lcd.clear();
 lcd.setCursor(0,1);
 lcd.print(pMIN);
 lcd.setCursor(5,1);
 lcd.print(pMID);
 lcd.setCursor(10,1);
 lcd.print(pMAX);
 /* //for test
 for (int i=0;i<200;i++){
 lcd.setCursor(0,0);
 lcd.print(SetupData[i]);
 delayms(1000);
 lcd.setCursor(0,0);
 lcd.print("           ");
 }*/
 beep(500);
}
//Функция опроса кнопок модуля дисплея 1602 с кнопками.
//кнопки подключены к аналоговому порту (0) ардуино, если их подключаете как то иначе - функцию необходимо переделать
int getKey() {
  int value = analogRead(0);
  if (value>1000) return 0; //ничего не нажато
  if (value>580) return 1; //SELECT //Автонастройка.
  if (value>380) return 2; //ЛЕВО
  if (value>200) return 3; //ВНИЗ //Уменьшить порог (увеличивается чувствительность)
  if (value>80) return 4; //ВВЕРХ //Увеличить порог (снижается чувствительность)
  if (value<50) return 5; //ВПРАВО
}
 
 
void beepOn() {
  pinMode(SPEAKER_PIN, OUTPUT);
}
void beepOff() {
  pinMode(SPEAKER_PIN, INPUT); 
}
 
void beep(int msec) {
 beepOn();
 delayms(msec);
 beepOff(); 
}
 
int lmax = 10;
int mmax = 80;
int mmaxDefault = 80;
int lmaxDefault = 10;
int LL=lmax; //Длина звук. сигнала, циклов
int MM=mmax; //Длина тишины, циклов.
 
     int vo = 0;
     int vi = 0;
     int vod = 0;
     int vid = 0;
     unsigned long v1 = 0;
int SendPulse() {
    //--------------------------
    //Начало импульса
    //Мощный ключ открыт, усилитель выключен.
    PORTD = B00000010; 
    //-------------------------
    //Ожидаем 100мкс: 
    delayMicroseconds(100);
    //Закрываем ключ:
    PORTD = B00001010;
    //Открываем Q1:
    delayMicroseconds(12); //Защитный интервал
    PORTD = B00001110;
    delayMicroseconds(7);
    PORTD = B00001100;
    delayMicroseconds(100);
    PORTD = B00001110;
    int r = analogRead(1);
    return r;
}
int tControl = 0; //может изменяться от 0 до 15
int maxControl = 10; //кол-во измерений для расчета - чем больше количество измерений, тем меньше влияние шума.
                    //При тестировании в доме я выставлял 12-15 измерений для получения стабильной работы.
                    //В поле достаточно двух
 
int getMidControl() {
  int gm = 0;
  for (int i=0;i<maxControl;i++) {
    gm = gm+LastData[i];
  }
  int res = gm/maxControl;
  return res;
}
 
char Str2[11] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,''};
int V;
void(* resetFunc) (void) = 0; //сброс
int setupcount = 150;
void loop() {
  int k =0;
  //tone(SPEAKER_PIN,400);
  noInterrupts();
  //lcd.setCursor(0,0);
  //lcd.print(t);
  
  //Бит 3 = 1, J3
  //Бит 2 = 1  J4
  //Бит 1 = 1, J9
  PORTD = B00001110;
  delayms(2000);
  int ssl=0; 
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("      ");
    lcd.setCursor(12,1);
    lcd.print("    ");
    lcd.setCursor(12,1);
    lcd.print(Sensv);
    int a = 0;
    int val;
    LL=9;
    while (1) {
      k = getKey();
     if(tControl>=maxControl) tControl=0;
    int t = SendPulse();
    LastData[tControl] = t;
    int s = getMidControl();
    tControl++;
    //для тестирования
    //pMID = 781;
    //s = 520;
    //==================
    //==================================
    // ОЗВУЧКА ЦЕЛИ          V
    //==================================
    if (LL<lmax && LL>0) LL--;
    if (LL==0 && MM>0) MM--;
    if (MM==0) {
      LL = lmax;
      MM = mmax;
    } 
    if (setupcount>0) {
      if (s<=pMID) pMID = pMID-1;
      setupcount--;
    }
    int signal=(pMID-Sensv)-s; //для средних значений
    //int signal=(pMIN-Sensv)-s; //для минимальных
    if (signal>0 && LL==lmax) {
      beepOn();
      //if (signal<50) slevel = 2-
      int slevel = 200-signal; //цифра влияет на чувствительность пищалки. 400- редко пикает, затем часто до писка. 50 - быстро, затем писк.
      if (slevel<5) slevel = 5;
      mmax = slevel/5;
      MM = mmax;
      LL--;
    }
    if (signal<0 && LL==lmax) {
      mmax = mmaxDefault;
      MM = mmax;
    }
    int ssk = signal;
    signal = signal/5;
    if (signal<0) signal = 0;
    for (int i=0;i<10;i++) {
    if (i<signal) Str2[i] = 0xff;
    else Str2[i] = ' ';
    }
    if (LL==0) beepOff();
    //==================================
    //==================================
    //if (a==0) val = s;
    lcd.setCursor(0,1);
    lcd.print(Str2);
    a++;
    if (k==4) {
      Sensv++;
      lcd.setCursor(12,1);
      lcd.print("   ");
      lcd.setCursor(12,1);
      lcd.print(Sensv);
      beep(100);
      delayms(100);
    }  
    if (k==3) {
      Sensv--; 
      lcd.setCursor(12,1);
      lcd.print("   ");
      lcd.setCursor(12,1);
      lcd.print(Sensv);
      beep(100);
      delayms(100);
    }
    if (k==1) {
      
      pMID = t;
      Sensv = 0;
      setupcount = 150;
      lcd.setCursor(12,1);
      lcd.print("   ");
      lcd.setCursor(12,1);
      lcd.print(Sensv);
      beep(200);
      delayms(500);
      
      //resetFunc();
    };
    delayMicroseconds(1000); 
 
    lcd.setCursor(0,0);
    lcd.print(vi);
    lcd.print('.');
    lcd.print(vid);
    lcd.print("V  ");
    lcd.print(ssl);
    lcd.print(' ');
        if (a>200) {
      a = 0;
     //1 раз за 200 циклов измеряем напряжение аккумулятора и выводим на экран
     V = analogRead(4);
     //V = V-73; //калибровка во вольтметру
     unsigned long vt = V*125l;
     vt = vt/64l-140;
     vi = vt/100;
     vid = vt/10%10;
     ssl = ssk/10;
     //vi = v1*4;
    }
    //lcd.print(vid);    
 
    //lcd.print(vi);       
  }
} 

Везде используется прямая запись в порт в целях экономии драгоценного времени – команда “PORTD = B00001110;” выполняется порядка 0.2мкс. AnalogRead выполняется чуть меньше 20 микросекунд – довольно долго, но АЦП меги 328 быстрее 16мкс оцифровать сигнал не может, поэтому 20 – не так уж и много для него. Из экономии времени не использую запись в определенные пины, а пишу целиком в порт, в связи с этим в те же пины, что используются дисплеем тоже попадают нули, глюков это никаких не вызвало, поэтому так и оставил. В фунцкии SendPulse можно побаловаться с изменением защитного интервала в пределах 8-15 мкс – изменится чувствительность в лучшую или в худшую сторону. Да, если будете экспериментировать со скетчем учтите, что в доме можно получить увеличение чувствительности, а в поле чувствительность уменьшится, или наоборот. Если кто то будет менять что то в скетче – следите за временем выполнения основного цикла. В запасе есть около 1000 микросекунд – это не мало, но фунцкии вывода на дисплей и различные арифметические операции сжирают время целыми милисекундами.

При разводке платы старался делать потолще дорожки (это был мой первый ЛУТ):

Работа с прибором:

maxControl – число измерений перед проверкой на наличие металла в зоне действия прибора. В принципе это значение – единственное, с чем можно экспериментировать после запуска. При уменьшении значения до 5 или 2-х – большое влияние на результат будут оказывать электромагнитные помехи (в населенных пунктах, например) и свойства грунта при поиске мелких предметов. Если увеличить до 15 – хорошая чувствительность наблюдается в условиях помех, но в поле значительно слабеет. Тут решайте по обстоятельствам. Я оставил 10 При старте посылается пара сотен импульсов, замеряются средние, максимальные и минимальные показания, на пару секунд появляется сообщение с этими цифрами. Если цифры в пределах 700-900 – всё должно работать. Если показывает везде 1024 – значит что то не подключили, либо проблема в катушке. Датчик необходимо залить эпоксидкой, иначе при попадании капель росы или дождя на датчик прибор будет вести себя неадекватно. Кнопка с кодом “1” используется для автонастройки. Подняли прибор, нажали

автонастройку, подождали 2 секунды – порядок, можно работать. При автонастройке чувствительность к мелким предметам может быть хуже где то на сантиметр – два, поэтому если ищете монетки и в земле мало всякого металлического мусора и осколков, воспользуйтесь кнопками ручной подстройки – чем меньше значение на дисплее, тем выше чувствительность.

Аккуратно с питанием Arduino – я подавал 12V на vin, или как вариант – можно сделать разьем питания и вставить его в Arduino. Аккумулятор подключал гелевый 12V 800mA. По расчетам должно хватать часов на 6 (потребление около 150 mA). Сам модуль металлоискателя потребляет ровно 100mA – если получается много больше, или меньше – нужно искать ошибки. Резистор на пищалке подбирал методом тыка, особо не задумывался, около 20 Ом – взял то что валялось дома. В принципе всё, может чего забыл – добавлю..

Архив с проектом в протеусе, скетч для Arduino и PDF для лута.

Решил добавить пару строк, т.к. возикают вопросы..

Сответую почитать тут
http://www.freeseller.ru/aelsam/metaloiskatel/2482-metalloiskatel.html

(по ссылке выше есть алгоритм функционирования, по нему я реализовывал функцию SendPulse)

тут
http://forum.cxem.net/index.php?showtopic=103963

ну и тут
http://forum.cxem.net/index.php?showtopic=47522&st=760

Станет понятнее как всё это работает.

Как я писал вначале – опыта у меня не много, схему дорабатываю. У кого есть осциллограф, обратите внимание на небольшие колебания по линии питания во время импульса, а так же во время звуковой сигнализации. Это нужно как то стабилизировать, даст прирост в чувствительности. В новой версии можно будет прошить, достать из Arduino контроллер и вставить в схему, ну или напрямую прошить программатором – кому как нравится, похожих устройств в сети хватает, с нуля разрабатывалась только программа. Осциллограф для настройки желателен, иначе придется подстраивать методом тыка.

По поводу полевых испытаний – с помощью прибора было откопано много, разного, и в основном ржавого, чувствительность в наших краях приходится занижать, т.к. вся земля усыпана шрапнелью и осколками снарядов с первой мировой. До этого пользовался только китайским MD-3010II (на биениях). Импульсник конечно оказался удобнее в разы. Был недостаток при поиске во время росы – когда датчик намокал пищал постоянно, но тщательная изоляция всё исправила. Да, любой металлодетектор на биениях в радиусе десятка метров от включенного импульсника сходит с ума и пищит постоянно)).


Ещё важный нюанс – не пытайтесь сунуть в работающий датчик

телефоны, планшеты и т д. Импульсы довольно мощные. Можно что то испортить.

Проверить, работает ли прибор так же очень просто – поднесите металлическую крышку

к датчику – услышите жужжание. Ещё можно взять динамик, закоротить концы

небольшим куском провода, поднести к датчику – услышите гул.

НЕ ПОДКЛЮЧАЙТЕ к прибору импульсные источники питания – что-нибудь сожгете.


0 thoughts on “Импульсный металлодетектор на Arduino. Изобретаем велосипед.

  • Андрей says:

    Заинтересовала ваша схема металлоискателя на ардуино, не могли бы вы по подробней описать как его собрать? на печатке что то не понятно куда подключить дисплей и в схеме этого нет ((

  • Сергей says:

    Вопрос к автору: с датчиками не эксперементировали? Вместо Планарной катушки не ставили обычную схожую по параметрам?
    И ещё вопрос: Можно ли прикрутить цветной TFT-дисплей типа: https://ru.aliexpress.com/item/3-2-inch-TFT-LCD-screen-module-Ultra-HD-320X480-for-Arduino-MEGA-2560-R3-Board/32604352555.html?spm=2114.13010308.0.0.BLhQIF&detailNewVersion=&categoryId=400401
    Если да то как? Какая распиновка или схема подключения? Чего в скетче поменять надо?

    • Да, пробовал обычную – глубже находит крупные предметы, но чувствительность ниже по отношению к предметам размером с монетку (хотя в некоторых случаях оно и лучше 🙂 )
      TFT дисплей, естественно, можно прикрутить, только насчет модификации скетча не подскажу, так как не держал подобный дисплей в руках. Надо смотреть примеры, и в моем скетче изменять функции вывода на экран. Плюс считать временные интервалы, так как передача данных на этот дисплей может занять время, особенно в режиме SPI, в связи с чем уменьшится количество импульсов в секунду, которые генерирует катушка.
      Вообще на Ардуинке очень трудно развернуться – достаточно медленная штука, и при больших частотах АЦП ошибается, если есть кое какие навыки, лучше используйте платки на базе STM32 (вроде этой – https://ru.aliexpress.com/item/ARM-Cortex-M3-mini-stm32-stm32F103RBT6-Cortex-development-board-72MHz-128KFlash-20KRAM/32327205488.html?spm=2114.41010308.4.14.3kpjoM ) АЦП у них тоже шустрее и точнее, плюс много функций можно реализовать на аппаратном уровне. Как раз занимаюсь разработкой ПО для аналога QUASAR IB с DD катушкой.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>