Дальномер и прерывание

alexey_and
Offline
Зарегистрирован: 03.03.2013

есть ик-дальномер sharp, реле, и такая программа: 


#define sensorIR 0            
float sensorValue, cm;    
int ledPin = 13;
int relPin = 24;

void setup() {
  pinMode(relPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  sensorValue = analogRead(sensorIR);
  cm = 10650.08 * pow(sensorValue,-0.935) - 10;
  delay(300);

  if (cm > 110)
{
    digitalWrite(ledPin,LOW);
    digitalWrite(relPin,LOW);

    
}
    else
{ 
    digitalWrite(ledPin,HIGH);
    digitalWrite(relPin,HIGH);
    delay(5000);
}
}

 

можно ли сделать включение реле по прерыванию? или с аналоговым пином это не реализовать?

maksim
Offline
Зарегистрирован: 12.02.2012

alexey_and пишет:
можно ли сделать включение реле по прерыванию?
Зачем?

alexey_and пишет:
или с аналоговым пином это не реализовать?
На аналоговом входе нет, но можно организовать на встроенном компараторе.

alexey_and
Offline
Зарегистрирован: 03.03.2013

мне кажется так было бы правильней. ведь пока работает delay, больше ничего выполняться не может

maksim
Offline
Зарегистрирован: 12.02.2012

Так не ипользуйте delay().

alexey_and
Offline
Зарегистрирован: 03.03.2013

а что использовать? мне нужно чтоб реле было включено в течение некоторого времени после того как cm станет > 110

 

maksim
Offline
Зарегистрирован: 12.02.2012

millis() или micros()  Мигаем светодиодом без delay()

А что должно происхотить если реле вкючилось на 5 секунд и пока эти пять секунд не прошли cm станет меньше 110? игнорировать и дальше отсчитывать свои 5 секунд?

И что если в течении этих пяти секунд cm станет меньше 110, а потом больше 110 ,то тогда что? тоже игнорировать? или начинать отсчет пяти секунд по новой?

alexey_and
Offline
Зарегистрирован: 03.03.2013

тз такое: как только расстояние становится меньше 110, включаем реле. когда расстояние становится больше 110, выключаем через некоторое время. у меня сейчас стоит 5 секунд, но может быть и минута. если за это время опять становится меньше 110, то ничего не происходит, реле как было включено, так и остается

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

alexey_and
Offline
Зарегистрирован: 03.03.2013

все еще мучаюсь с датчиком :(

вот такой код:

#define sensorIR 7            
float sensorValue, cm;    

int pres = 0;    //переменная для определения состояния (меньше 50 или больше)

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

void loop() {
  sensorValue = analogRead(sensorIR);
  cm = 10650.08 * pow(sensorValue,-0.935) - 10;

  if (cm < 50)
  {
    pres = 0;
        Serial.println(pres);
  }
    else
    {
   pres = 1;
       Serial.println(pres);
    }
}

как его заставить выдавать 0 или 1 не постоянно, а только при смене 0 на 1 и наоборот?

step962
Offline
Зарегистрирован: 23.05.2011

alexey_and пишет:

как его заставить выдавать 0 или 1 не постоянно, а только при смене 0 на 1 и наоборот?

Строчки 16-17 переписываем в виде

if (pres==1) { // выполнение только в момент перехода с 1 на 0
  pres = 0;
  Serial.println(pres); 
}

Со строчками 21-22, надеюсь, справитесь самостоятельно?

PS: И чтобы в будущем такие вещи не вводили в ступор, попробуйте ответить на вопрос:

Что такое событие и чем оно отличается от состояния?

maxi_10
Offline
Зарегистрирован: 05.01.2012

Ввести ещё одну переменную и по ее состоянию отсылать значения "pres"

код писал не в редакторе поэтому возможны синтаксические ошибки.

но общий принцип такой:

 

#define sensorIR 7            
float sensorValue, cm;    
int Flag = 0;
int pres = 0;    //переменная для определения состояния (меньше 50 или больше)

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

void loop() {
  sensorValue = analogRead(sensorIR);
  cm = 10650.08 * pow(sensorValue,-0.935) - 10;

  if (cm < 50)
  {
    if (Flag = 0)
    {
    Flag = 1;
    pres = 0;
        Serial.println(pres);
     }
 }
    else
    {
    if (Flag = 1)
    {
    Flag = 0;
    pres = 1;
        Serial.println(pres);
     }
    }
}

ИЛИ

#define sensorIR 7            
float sensorValue, cm;    
int Flag = 0;
int pres = 0;    //переменная для определения состояния (меньше 50 или больше)

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

void loop() {
  sensorValue = analogRead(sensorIR);
  cm = 10650.08 * pow(sensorValue,-0.935) - 10;

  if (cm < 50&&Flag = 0)  {pres = 0; Flag = 1; Serial.println(pres); }
  if (cm >= 50&&Flag = 1)  {pres = 1; Flag = 0; Serial.println(pres); }
}

 

maxi_10
Offline
Зарегистрирован: 05.01.2012

Пример step962 более верен.

мой нидний пример сгодится в том случае если в порт будет передаватся значение "cm".

 

 

alexey_and
Offline
Зарегистрирован: 03.03.2013

 

все коды работают, спасибо огромное! сам думал надо дополнительной переменной, но не смог додуматься как ее использовать. поэтому пока оставил нижний код. теперь ломаю мозг как приспособить millis, чтоб когда cm становится больше 50, pres становился 1 с задержкой. прошу строго не судить за нубские вопросы, последний раз занимался программированием лет 13 назад в школе, на бейсике под досом :) но есть огромное желание освоить ардуину :)

PS 

step962 пишет:

И чтобы в будущем такие вещи не вводили в ступор, попробуйте ответить на вопрос:

Что такое событие и чем оно отличается от состояния?

событие в моем понимании - это смена состояния

step962
Offline
Зарегистрирован: 23.05.2011

alexey_and пишет:

событие в моем понимании - это смена состояния

В моем - тоже.

И поскольку это смена, его отслеживают по различию состояний при предыдущем проходе цикла и при текущем. Как только обнаружили, что на предыдущем проходе цикла состояние было 0, а сейчас 1, говорим себе - ага, событие. И делаем то, что нам необходимо. А если и тогда 0 был, и сейчас (или - что то же самое - 1 и 1), то точно так же говорим себе - ага, события не было. И ничего не делаем. 

alexey_and
Offline
Зарегистрирован: 03.03.2013

это более-менее понятно. как сделать задержку никак не могу понять, весь мозг сломал

maksim
Offline
Зарегистрирован: 12.02.2012

Не надо ничего ломать, просто посмтрите пример Мигаем светодиодом без delay(), который я вам дал выше. И покажите весь код.

alexey_and
Offline
Зарегистрирован: 03.03.2013

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

maksim
Offline
Зарегистрирован: 12.02.2012

Поверьте, вам нужно тоже самое, код покажите. Не понятно только зачем вам это, если на момент задержки у вас все должно игнорироваться...

alexey_and
Offline
Зарегистрирован: 03.03.2013

#define sensorIR 7            
float sensorValue, cm;    
int Flag = 0;
int pres = 0;    //переменная для определения состояния (меньше 50 или больше)
int ledPin = 13;

unsigned long currentTime;
unsigned long loopTime;

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

  currentTime = millis();      
  loopTime = currentTime; 

}

void loop() {
  sensorValue = analogRead(sensorIR);
  cm = 10650.08 * pow(sensorValue,-0.935) - 10;

  currentTime = millis();

  if (cm < 50 && Flag == 0)
    {
    pres = 0; 
    Flag = 1; 
    Serial.println(pres); 
    digitalWrite(ledPin,LOW);
    }

 
   if (cm >= 50 && Flag == 1)
    {
          Flag = 0; 
        if(currentTime >= (loopTime + 000)){         
    pres = 1; 
    Serial.println(pres); 
    digitalWrite(ledPin,HIGH);
    loopTime = currentTime;                     
    }
    }
}

 

alexey_and
Offline
Зарегистрирован: 03.03.2013

фигня полная получается

 

maksim
Offline
Зарегистрирован: 12.02.2012

000 - это вы конечно ошиблись

alexey_and
Offline
Зарегистрирован: 03.03.2013

менял просто, а перед вставкой сюда не проверил. в реале там 3000 стоит

maksim
Offline
Зарегистрирован: 12.02.2012

Еще раз опишите как себя должен вести светодиод относительно расстояния.

alexey_and
Offline
Зарегистрирован: 03.03.2013

принцип такой: если расстояние больше 50, диод горит. если становится меньше 50, гаснет. если снова больше 50, ждет некоторое время (допустим 3 секунды) и загорается. 

maksim
Offline
Зарегистрирован: 12.02.2012

Вы опять же не полностью описали алгоритм работы ,тогда примерно так:

#define sensorIR 7   
#define ledPin 13  

bool flag = 0, pres = 0;
unsigned long prev_millis;

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

void loop() 
{
  float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;

  if(cm >= 50 && flag == 1)
  {
    flag = 0;
    pres = 0;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 0)
  {         
    pres = 1; 
    Serial.println(pres); 
    digitalWrite(ledPin, HIGH);                    
  }

  if(cm < 50 && flag == 0)
  { 
    flag = 1; 
    Serial.println(pres); 
    digitalWrite(ledPin, LOW);
  }
}

 

alexey_and
Offline
Зарегистрирован: 03.03.2013

теперь получается, если расстояние меньше 50 в течение 3 секунд - загорается. как только больше - сразу гаснет

maksim
Offline
Зарегистрирован: 12.02.2012

Должно быть наоборот: если в течении 3 секунд расстояние больше 50, то светодиод загорается, если расстояние меньше 50 то гаснет. Заначит не верен ваш расчет 10650.08 * pow(analogRead(sensorIR), -0.935) - 10 . Но если раньше времени расстояние станет менньше 50 то светодиод все равно загорится, поэтому, что бы этого не происходило надо сделать так:

#define sensorIR 7   
#define ledPin 13  

bool flag = 0, pres = 0;
unsigned long prev_millis;

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

void loop() 
{
  float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;

  if(cm >= 50 && flag == 1)
  {
    flag = 0;
    pres = 1;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 1)
  {         
    pres = 0; 
    digitalWrite(ledPin, HIGH);                    
  }

  if(cm < 50 && flag == 0)
  { 
    flag = 1; 
    pres = 0;
    digitalWrite(ledPin, LOW);
  }
}

именно по этому я вам и говорю, что вы не полностью описываете алгоритм.

alexey_and
Offline
Зарегистрирован: 03.03.2013

расчет точно верный, даже с рулеткой проверял. погрешность конечно есть, но для меня не критичная, сантиметра 3-5

alexey_and
Offline
Зарегистрирован: 03.03.2013

все то же самое... еще добавились ложные включения диода

Snubist
Offline
Зарегистрирован: 18.02.2013

Однажды мучался с датчиком уровня, там растояние было в уровне воды от пола, а не от датчика.

Может у вас тоже самое?

Измените растояние условиях на 30 см, и проверте на каком растоянии он отработает на 30 или 70 сантиметрах.

 

maksim
Offline
Зарегистрирован: 12.02.2012

alexey_and пишет:

все то же самое... еще добавились ложные включения диода

Еще раз перекопируйте. И это не ложные срабатывания - это срабатывания по описанному вами алгоритму, вы же не написали что должно происходить, если в течении этих 3-х секунд расстояние станет меньше 50... а значит такой вариант просто не учитываеся в этом коде.

alexey_and
Offline
Зарегистрирован: 03.03.2013

Snubist, срабатывает точно на том уровне, какой пропишешь

maksim, точно проверил, залит новый код. попробую еще раз описать алгоритм: если расстояние больше 50, диод горит. если становится меньше 50, гаснет. если снова больше 50, ждет некоторое время (допустим 3 секунды) и загорается. если в момент пока он ждет, становится меньше 50 - не загорается. то есть аналогично тому, если б использовался delay. даже не знаю чем еще можно дополнить алгоритм...

maksim
Offline
Зарегистрирован: 12.02.2012

1. Для чего это изначальное:: если расстояние больше 50, диод горит?
2. Как раз если бы была просто задержка, то светодиод бы зажегся и сразу же бы потух.

А что должно произойте если в течении этих 3 секунда расстояние стало меньше 50, а затем опять больше?

maksim
Offline
Зарегистрирован: 12.02.2012

Алгоритм: Если расстояние меньше 50см, то светодиод отключается. Если расстояние больше или равно 50см в течении 3 секунд, то светодиод включается и горит до тех пор пока расстояние не станет меньше 50см. Если в момент ожидания 3 секунд расстояние стало меньше 50см, то светодиод не включается, если же опять стало больше 50см, то отсчет 3 секунд начинается заново.

#define sensorIR 7   
#define ledPin 13  

bool flag = 0, pres = 0;
unsigned long prev_millis;

void setup() 
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() 
{
  //float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;
  
  static int cm;
  if(Serial.available()) cm = Serial.parseInt();
  
  if(cm >= 50 && flag == 0)
  {
    flag = 1;
    pres = 1;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 1)
  {         
    pres = 0; 
    digitalWrite(ledPin, HIGH);                    
  }

  if(cm < 50 && flag == 1)
  { 
    flag = 0; 
    pres = 0;
    digitalWrite(ledPin, LOW);
  }
}

Я проверяю через сериал-монитор, вводя значения расстояния 49 и меньше или 50 и больше. Можете самим проверить. Если хотите работать с датчиком, то закомментируйте 17 и 18 строки и раскомментируйте 15 строку.

Snubist
Offline
Зарегистрирован: 18.02.2013

Может просто проскакивают ложные показания с датчика?

Попробуйте их усреднить. 

alexey_and
Offline
Зарегистрирован: 03.03.2013

1. диод просто для индикации. потом сюда добавится реле. если расстояние больше 50 (в реале будет 110, 50 это чтоб рукой махать - тестировать) значит человека нет, следовательно реле выключено. (а выключено оно, как ни странно, когда говоришь ноге HIGH. возможно потом разберусь с этим, это не самая главная проблема)

2. программа, которая в самом первом посте точно работает как надо, но использовать делей никак нельзя, ардуино будет управлять не только одним этим реле

3. задержка нужна для того, чтоб отфильтровать возможные мельтешения человека. если в течение 3 сек стало меньше - диод по прежднему ждет в выключенном состоянии, если затем больше - по истечении этих 3 сек загорается

 

в итоге так: человек зашел - реле включилось. вышел - подождало некоторое время и выключилось. ждем для того чтобы исключить поворот человека боком, махание руками, выпадение из зоны видимости датчика и тп.

alexey_and
Offline
Зарегистрирован: 03.03.2013

Snubist, ложные точно не проскакивают, довольно долго мониторил через сериал, пишет расстояния в пределах погрешности

maksim, теперь работает как надо, огромное спасибо! завтра попробую добавить реле

maksim
Offline
Зарегистрирован: 12.02.2012

Тогда просто еще добавьте в сетап включение светодиода:

#define sensorIR 7   
#define ledPin 13  

bool flag = 0, pres = 0;
unsigned long prev_millis;

void setup() 
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
}

void loop() 
{
  float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;
  
  //static int cm;
  //if(Serial.available()) cm = Serial.parseInt();
  
  if(cm >= 50 && flag == 0)
  {
    flag = 1;
    pres = 1;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 1)
  {         
    pres = 0; 
    digitalWrite(ledPin, HIGH);                    
  }

  if(cm < 50 && flag == 1)
  { 
    flag = 0; 
    pres = 0;
    digitalWrite(ledPin, LOW);
  }
}

 

alexey_and
Offline
Зарегистрирован: 03.03.2013

кстати что еще интересно, после подачи питания проходит секунд 20 прежде чем конструкция начинает работать

maksim
Offline
Зарегистрирован: 12.02.2012

У вас почему-то долго стартует бутлоудер. А дуина какая?

alexey_and
Offline
Зарегистрирован: 03.03.2013

nano 3.0. раньше такого вроде не было. но впринципе это не важно

alexey_and
Offline
Зарегистрирован: 03.03.2013

забыл спросить: через 50 дней дуина зависнет? 

maksim
Offline
Зарегистрирован: 12.02.2012

Нет.

alexey_and
Offline
Зарегистрирован: 03.03.2013

странное дело. изменил расстояние с 30 на 110, и ledPin все время HIGH. добавляю запись в сериалпорт - работает как надо.

вот коды: вот так работает

#define sensorIR 7   
#define ledPin 13  
int relPin = 3;

bool flag = 0, pres = 0;
unsigned long prev_millis;

void setup() 
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(relPin, OUTPUT);
}

void loop() 
{
  float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;
    Serial.print("Centimeters: ");
    Serial.println(cm);
//  static int cm;
//  if(Serial.available()) cm = Serial.parseInt();
  
  if(cm >= 110 && flag == 0)
  {
    flag = 1;
    pres = 1;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 1)
  {         
    pres = 0; 
    digitalWrite(ledPin, LOW); 
    digitalWrite(relPin, HIGH);    
  }

  else if(cm < 110 && flag == 1)
  { 
    flag = 0; 
    pres = 0;
    digitalWrite(ledPin, HIGH);
    digitalWrite(relPin, LOW);
  }
}

а так - светодиод все время горит

#define sensorIR 7   
#define ledPin 13  
int relPin = 3;

bool flag = 0, pres = 0;
unsigned long prev_millis;

void setup() 
{
 // Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(relPin, OUTPUT);
}

void loop() 
{
  float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;
//    Serial.print("Centimeters: ");
//    Serial.println(cm);
//  static int cm;
//  if(Serial.available()) cm = Serial.parseInt();
  
  if(cm >= 110 && flag == 0)
  {
    flag = 1;
    pres = 1;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 1)
  {         
    pres = 0; 
    digitalWrite(ledPin, LOW); 
    digitalWrite(relPin, HIGH);    
  }

  else if(cm < 110 && flag == 1)
  { 
    flag = 0; 
    pres = 0;
    digitalWrite(ledPin, HIGH);
    digitalWrite(relPin, LOW);
  }
}

 

еще интересно, почему не работает вот так:

  if(millis()-prev_millis >= 500 && cm < 100 && flag == 1)
  { 
    flag = 0; 
    pres = 0;
    digitalWrite(ledPin, HIGH);
    digitalWrite(relPin, LOW);
  }
}

все-таки защита от случайного срабатывания нужна, проскакивают иногда случайные неправильные значения. либо действительно каким-то образом усреднять показания датчика. либо добавить кондер к питанию дальномера. америкосы вроде так делают

alexey_and
Offline
Зарегистрирован: 03.03.2013

upd: кондер вроде помог. пока тестирую

Snubist
Offline
Зарегистрирован: 18.02.2013

вместо кондера можно программно


#define sensorIR 7   
#define ledPin 13  
int relPin = 3;
float z[10];
bool flag = 0, pres = 0;
unsigned long prev_millis;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(relPin, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly: 
float Summ=0;  
float cm = 10650.08 * pow(analogRead(sensorIR), -0.935) - 10;
  for (int i=1;i<10;i++)
    {
      z[i]=z[i-1];
      Summ+=z[i];
    }
  z[0]=cm;
  Summ+=cm;
  cm=Summ/10;
      Serial.print("Centimeters: ");
    Serial.println(cm);
//  static int cm;
//  if(Serial.available()) cm = Serial.parseInt();
  
  if(cm >= 110 && flag == 0)
  {
    flag = 1;
    pres = 1;
    prev_millis = millis();
  }

  if(millis()-prev_millis >= 3000 && pres == 1)
  {         
    pres = 0; 
    digitalWrite(ledPin, LOW); 
    digitalWrite(relPin, HIGH);    
  }

  else if(cm < 110 && flag == 1)
  { 
    flag = 0; 
    pres = 0;
    digitalWrite(ledPin, HIGH);
    digitalWrite(relPin, LOW);
  }
}

в строках 4,19,26 заменить 10 на нужное усреднение

alexey_and
Offline
Зарегистрирован: 03.03.2013

с усреднением поинтересней работает, но кондер уже выпаивать не буду :) все четко работает, не глючит, огромное спасибо!

alexey_and
Offline
Зарегистрирован: 03.03.2013

итак, вот шедевр: http://youtu.be/c_UkIxnuS-Y

https://www.dropbox.com/s/oht35ca5ub8zoh5/DSC03114.JPG

 

https://www.dropbox.com/s/hb2pi63aagvnbn6/DSC03115.JPG

https://www.dropbox.com/s/ha6tiu9hvoh92k0/DSC03117.JPG

https://www.dropbox.com/s/9ubjxo44po7esqj/DSC03121.JPG

иногда срабатывает при включении или выключении других выключателей. как бороться?

ps. на очереди ванна. думаю использовать два дальномера в проходе - чтоб считать людей. либо что-то вроде этого http://www.youtube.com/watch?v=L5rrU_09Rqo

Snubist
Offline
Зарегистрирован: 18.02.2013

Молодец!!!

По поводу срабатывания, проверь (увеличь мощность блока) питание.

Про ванну, посмотри http://arduino.ru/forum/proekty/umnye-tualet-i-vannaya

alexey_and
Offline
Зарегистрирован: 03.03.2013

спасибо :) на торчащие провода не смотрите - в процессе ремонта все будет убрано

щас запитано через зарядник от мобильника с юсб-хвостом. 5в, 0.65а

тоже думал что в блоке дело, запитал автономно (от ноута) - та же хрень. то есть помеха идет со стороны реле. видимо надо опто-развязывать. хочу попробовать такое реле - http://www.chipdip.ru/product/s202t02/    есть смысл?

Snubist
Offline
Зарегистрирован: 18.02.2013

А какой модуль реле сечас стоит?

На счет   оптореле, из даташита S102T01 / S102T02 S202T01 / S202T02

Ток 50mA, напряжение 6V/

А у ардуино выход 40mA, напряжение 5V

alexey_and
Offline
Зарегистрирован: 03.03.2013

модуль абсолютно нонеймовый китай

на счет оптореле понял, надо искать что-то более подходящее