Подскажите пожалуйста новичку

xellow
Offline
Зарегистрирован: 12.03.2016
void check_start()
088 {
089 if (digitalRead(start_in) == 0 && left_start_try == 0) // 1 is command for start  - 1  значит импульс старта пришел с дистанционного управления <--- тут определяется полярность импульса запуска
090   {
091    left_start_try = 3;   // указываем что нужно попытаться трижды запустить движок
092   }
093  
094  
095  if ( left_start_try > 0 )  
096  {
097     digitalWrite(engine_out, HIGH);   //включаем зажигание
098     digitalWrite(secpower_out, HIGH); //включаем печку фары итд
099     delay(3000); // останавливаем код на 3 секунды чтобы бензонасос набрал давление, все датчики включились

Добрый день. Есть устройство которое при определённом событии отправляет импульс на ардуину и запускается скетч. Но проблема в том, что при подаче питания устройство так же кратковременно выдаёт этот импульс. Как отстроить скетч от этого срабатывания? Кусочек скетча с командой от устройства (строка 089) приложил.

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Поставить в setup() задержку, пока внешний сигнал устаканится - delay(xxx).

xellow
Offline
Зарегистрирован: 12.03.2016

Я не совсем верно написал коммент. Сигнал от внешнего устройства не запускает скетч, а так сказать задаёт определённое событие в скетче, а setup запускается при подаче питания на Ардуину - питание у устройства и Ардуины разные. Что бы не было путаницы валожу весь скетч...

unsigned long ENGINE_WARM_TIME = 900000; //В миллисекундах время работы мотора после запуска 1200000 миллисекунд = 20 минут
int ENGINE_START_MAX_TIME = 5; //В секундах масимальное время работы стартера типично 3 -10 сек
 
 
 
 
// select pins for input
const int hand_brake_in  = 3;  //d3 adruino Контакт стояночного тормоза "-"
const int start_in  = 4;  //d4 adruino Сигнал команды на старт от мобильного телефона вибра звонка
const int sharging_in  = 5;  //d5 adruino
 
const int sharging_on = 1; // 0 когда во время работы генератора или от датчика давления масла на этом входе низкое состояние , 1 когда на лампе генератора при работе генератора высокое состояние
const int hand_brake_on = 0; //  0= поднятый датчик ручника замыкает на массу тормоз активен (жигули), 1= датчик ручника в поднятом состоянии выдает высокий уровень напряжения 
 
// select pins for output
const int starter_out  = 6;  //d6 adruino реле стартера
const int engine_out  = 8;  //d7 adruino зажигание
const int secpower_out  = 7;  //d8 цепи вторичного питания печка, фары итд
const int status_out  = 9;  //d9 светодиод статуса системы  горит = система готова к работе(включена, ручник стоит). одно мигание запущен с первой попытки 2 со второй 3 с третьей 
                            //светодиод подключать с этого пина на массу
const int door_out  = 10; //имитация открытия вод. двери
const int gsm_out  = 11; //выключение сигнала GSM
 
// variable for actual mode
int actual_mode = 0; //  2 engine started  
int left_start_try = 0;  // переменная для хранения остатка числа попыток запуска 
unsigned long last_start_time = 0; //время в тысячных секунды когда был запущен движок 
 
 
 
 
 
void setup() {
  
  //---- настройка входов и выходов контроллера --------------  
// init selected pins
  pinMode(hand_brake_in, INPUT); // enable input 
  pinMode(start_in, INPUT); // enable input 
  pinMode(sharging_in, INPUT); // enable input 
 
// init outputs
  pinMode(starter_out, OUTPUT); // enable output
  digitalWrite(starter_out, LOW); //set digital 0
 
  pinMode(engine_out, OUTPUT); // enable output
  digitalWrite(engine_out, LOW); //set digital 0
 
  pinMode(secpower_out, OUTPUT); // enable output
  digitalWrite(secpower_out, LOW); //set digital 0 
 
  pinMode(status_out, OUTPUT); // enable output
  digitalWrite(status_out, LOW); //set digital 0 
 
  pinMode(door_out, OUTPUT); // enable output
  digitalWrite(door_out, LOW); //set digital 0
 
  pinMode(gsm_out, OUTPUT); // enable output
  digitalWrite(gsm_out, HIGH); //set digital 0
  //---- конец настройки входов и выходов контроллера --------------    
  
 
}
 
 
void loop()
{
  //---- собственно это и есть весь код программы --------------
if (actual_mode < 2 )    //если двигатель не запущен
        check_start();  //Управляем режимом запуска
//код условия обязательно включать в фигурные скобки если должно быть выполнено несколько инструкций, в данном случае одна, поэтому скобки опущены        
      
else   // иначе
        {
        check_for_shutdown(); //Управляем режимом ожидания окончания прогрева
        } //но хорошая привычка фигурные скобки ставить всегда
 
      
 set_status_led(); //независимо от режима Управляем светодиодом статуса
 
  //------------------конец основного кода -----------------
 }
 
 
 
 
 
  //---- а дальше идут используемые функции и подфункции -------------- 
 
void check_start() 
{
if (digitalRead(start_in) == 1 && left_start_try == 0) // 1 is command for start  - 1  значит импульс старта пришел с дистанционного управления <--- тут определяется полярность импульса запуска
  {
   left_start_try = 3;   // указываем что нужно попытаться трижды запустить движок
   delay(3000);
   digitalWrite(gsm_out, LOW);
   delay(1000);
   digitalWrite(gsm_out, HIGH); 
  } 
 
 
 if ( left_start_try > 0 )   
 {
    digitalWrite(engine_out, HIGH);   //включаем зажигание 
    delay(5000); // останавливаем код на 3 секунды чтобы бензонасос набрал давление, все датчики включились
    
    if (digitalRead(hand_brake_in) != hand_brake_on) //без ручника не делаем запуск
    {
      actual_mode = 2; // заканчиваем попытки запустить движок
      left_start_try = 0; //без ручника другие попытки бесполезны
      return;  
    }
    
  
   if (digitalRead(sharging_in) != sharging_on  )    // проверяем что генератор не работает 
   {
      do_start(); // пытаемся запустить движок
      left_start_try = left_start_try - 1 ; //уменьшаем число попыток
 
        if (left_start_try == 0 ) 
          actual_mode = 2; // заканчиваем попытки запустить движок
    } // конец проверки что ручник стоит а генератор не работает
}   
 
      
} // конец процедуры старта
 
 
void do_start()
{
 //тут будет запуск движка    
 
//----------------------------------------------------------------------------------- цикл стартера 
    digitalWrite(starter_out, HIGH); //включаем стартер 
    for (int secs=0; secs <= ENGINE_START_MAX_TIME ; secs++) // 
      {
      delay(1000); // и продолжаем его держать включенным 1 секунду
      if (digitalRead(sharging_in) == sharging_on) //если зарядка пошла то 
               break;  // прерываем цикл
      }
//----------------------------------------------------------------------------------- 
    digitalWrite(starter_out, LOW); //отключаем стартер 
 
    if (digitalRead(sharging_in) == sharging_on) //еще раз смотрим что  зарядка пошла  
    {
      actual_mode = 2; //Запоминаем что движок запущен
      last_start_time = millis(); // запоминаем время запуска движка
    delay(3000);
    digitalWrite(secpower_out, HIGH); //включаем печку фары итд  
    }
    
    else
    {
      digitalWrite(engine_out, LOW);   //выключаем зажигание что бы разблокировать реле стартера
      delay(3000); // останавливаем код на 3 секунды чтобы бензонасос набрал давление
    }
    
    
}
 
void check_for_shutdown() 
{
//сюда мы попадаем когда контроллер считает что движок работает
 
    if (digitalRead(sharging_in) != sharging_on  ) //проверяем что движок случайно не заглох
          do_shutdown(); 
      
    if (digitalRead(hand_brake_in) != hand_brake_on  ) //проверяем что если злодеи сняли с ручника то глушим мотор
      {
         do_shutdown();
         left_start_try =0; // и больше не заводим
      }
  
//тут будем контролировать чтоб если забыли машинку заведенной она не молотила до скончания бензобака а выключалась через какоето время
 
 
    if (actual_mode != 0  ) //значит не выключили заглохший двигатель только что 
    {
      
      // милисекунды в ардуино обнуляются каждые 49 суток
      // для того чтобы в случае если запуск произошел в течении последних 20 минут до обнуления мотор не молотил еще 49 суток
      //  нужно обнулить и время старта, при этом мотор максимум будет прогреваться вдвое больше обычного 
      if (millis() < last_start_time)
          {
          last_start_time = 0;
          }   
      
      
      if (millis() > last_start_time+ENGINE_WARM_TIME)  // если текущее время больше чем время старта + время прогрева
         {
         do_shutdown();
         left_start_try =0; // и больше не заводим
         }
    }
 }
 
 
 
void do_shutdown()
{
       
 {
        digitalWrite(secpower_out, LOW); //выключаем печку фары итд         
        digitalWrite(engine_out, LOW); //вырубаем зажигание
        actual_mode = 0;  // движок выключили запомним это
        last_start_time = 0; // ну и забудем о том что он был включен
        digitalWrite(door_out, HIGH);   
        delay(3000); 
        digitalWrite(door_out, LOW); 
 }
}
 
void set_status_led()
{
if (digitalRead(hand_brake_in) != hand_brake_on  ) //Если ручник не стоит то выкл светодиод нечего ему в пути мигать попусту
  {
  digitalWrite(status_out, LOW);
  return; //прерываем выполнение функции
  }
  
if (actual_mode ==  2) //Движок запущен
     status_led_flash(); // показываем с какой попытки был прошлый запуск
else
     digitalWrite(status_out, HIGH); // постоянное свечение - показываем что готов к следующему запуску
 
}
 
 
 
void status_led_flash()
{
static unsigned long big_interval = 0;
static unsigned long flash_interval = 0;
unsigned long flash_count = 0;
flash_count = 3 - left_start_try;  //сколько раз мигать
 
 
//1 секндный интервал
if (millis() > flash_interval + 1000L)
      flash_interval = millis() ; //задаем счетчие от 0 до 1 секунды
 
//задаем 6 секунд 
if (millis() > big_interval + 6000L)
     {
      big_interval = millis() ;  //задаем счетчие от 0 до 10секунд
      flash_interval = big_interval;
     }  
 
 
 
if (millis() <  big_interval +  flash_count * 1000L) // делим 6 секндный интервал на два периода, в первый мигаем количество раз соответствующее числу израсходованных попыток
  { //мигает 
    if (millis() > flash_interval + 500L) 
      digitalWrite(status_out, HIGH);
    else
      digitalWrite(status_out, LOW);
 
  } 
else 
  { // негорит
    digitalWrite(status_out, LOW); //set digital 0 
  }
}
 
faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

И кто-то будет Ваш скетч изучать, да ещё залитый текстом, а не как положено? :) Описывайте суть проблемы: что за чем запускается, что куда чего даёт и что из этого лишнее. Если у Вас дуня постоянно включена и кушает АКБ, то дело Ваше. :)

Если у Вас что-то там включается при уже включенной дуне и появляется паразитный сигнал, то:

а) схема неверно сделана;
б) устройство кривое;
г) потребуется отслеживать отдельно включение того устройства и и не верить ему некторое время.

xellow
Offline
Зарегистрирован: 12.03.2016

Суть проблемы: Есть GSM устройство, которое даёт импульс положительной полярности в Ардуину при поступлении СМС. Но косяк устройства в том, что при подаче на него питания оно тоже кратковременно подаёт этот импульс. Собственно вырезку из скетча я давал в первом сообщении:

void check_start()
088 {
089 if (digitalRead(start_in) == 0 && left_start_try == 0) // 1 is command for start  - 1  значит импульс старта пришел с дистанционного управления <--- тут определяется полярность импульса запуска
090   {
091    left_start_try = 3;   // указываем что нужно попытаться трижды запустить движок
092   }
093  
094  
095  if ( left_start_try > 0 )  
096  {
097     digitalWrite(engine_out, HIGH);   //включаем зажигание
098     digitalWrite(secpower_out, HIGH); //включаем печку фары итд
099     delay(3000); // останавливаем код на 3 секунды чтобы бензонасос набрал давление, все датчики включились

digitalRead(start_in) - это и есть сигнал от устройства. 

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

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Логика работы конечного автомата слишком сильно разнесена по функциям и не очевидна. Код не офрмлен как положено и поэтому труден для всоприятия. Пока нашел что режим у вас устанавливается в зависимости от состояния ручника, но движок заводится ПРЕДВАРИТЕЛЬНО .. так точно надо?

Вам тоже советую прочитать Паронджанов В.Д. "Занимательная информатика", взять можно тут drakon.su, как и программки к языку. Псоле чего не полениться и выписать полноценную ДРАКОН-схему вашего конечного автомата. Найти и поправить ошибки станет возможно "левой задней пяткой", как мне кажется. Книжка веселая и доходчивая, легко осваивается детьми в возрасте 9-10лет за пару дней, впрочем как и "ИС ДРАКОН" с указанного сайта. У него есть месяц бесплатного пользования - вам вполне достаточно, а понравится - можете и оплатить автору его 300-500руб "пенсионных". Он того вполне заслуживает. :)

xellow
Offline
Зарегистрирован: 12.03.2016

Давайте обстрагируемся от этого скетча. Возьмём простой пример. Есть кнопка, при замыкании которой срабатывает реле.

Например такой скетч:

int button = 2;

int rel = 8;

void setup() {

pinMode(rel, OUTPUT);

pinMode(button, INPUT);

}

void loop(){

if (digitalRead(button) == HIGH) {

digitalWrite(rel, HIGH);

}

else {

digitalWrite(rel, LOW);

}

}

Вопрос: как выполнить условие, что бы реле работало, только при фиксации кнопки более 1 секунды?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

У Калапуция, как бы к нему не относиться, есть прекрасный код для кнопки, Он привязан в шапке форума.

...

Если короче, то так: при обнаружении  Вашего сигнала, Вы не кидаетесь, как ошпаренный выполнять команду, а ЗАПОМИНАЕТЕ время прихода сигнала  и ВЗВОДИТЕ ФЛАГ ожидания.

При следующем вызове, а мы понимаем, что loop() вызывается постоянно, раз-за-разом, Вы проверяете смостояние волшебного Вашего сигнала, если прошло некое разумное (на Ваше усмотрение, 10-200 мс) время, а сигнал все еще поступает, то Вы приходите к важному в Вашей жизни решению - это не на...балово, а сигнал, внезапно, НАСТОЯЩИЙ!!! Вот тут уж не отвертишься - ноги в руки и быстро выполнять код, что-там-у-Вас, запуск двигателя?

 

Я понятно объяснил? Если нет - прочтите, как Калапуций написал свой "титановый велосипед для тактовой кнопки" - это хороший пример, чтобы разобраться в вопросе.

xellow
Offline
Зарегистрирован: 12.03.2016

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

в процедуре чек-старт() завести static переменную типа такой: first_sig_detect_time ("запомненное время"), При первом появлении сигнала  (если эта переменная равна 0 И что-там-у-Вас в ифе в первом посте) просто запомнить в ней millis(); При следующем, если "текущее_время" - "запомненное" БОЛЬШЕ "проверочного" И "сигнал есть" - то начать работать, Иначе, если сигнала нет, то сбросить в 0 "запомненное время".

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

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

wdrakula пишет:

У Калапуция, как бы к нему не относиться, есть прекрасный код для кнопки, Он привязан в шапке форума.

есть у Клапауция...

класс титановый велосипед для тактовой кнопки.

класс титановый велосипед для delay без delay().

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Все кому сейчас под 50 читали Лема, совок тогда общий на всех был.

Я нарочно коверкаю твой ник, так смешнее. Кому нужно - поймет, кто не понял - ну и пусть.

xellow
Offline
Зарегистрирован: 12.03.2016
Долго морщил лоб))) Получилось это... поправьте плиз что не так...

[code]
void check_start()
{ 
static const unsigned int retention = 2000; // длительность отслеживания нажатия и удержания.
static int first_sig_detect_time = 0; // длительность отслеживания нажатия и удержания.
{
if (digitalRead(start_in) == 1 && left_start_try == 0 && first_sig_detect_time == 0) // 1 is command for start  - 1  значит импульс старта пришел с дистанционного управления <--- тут определяется полярность импульса запуска
  {
  first_sig_detect_time = millis();
  }
else
{
  first_sig_detect_time = 0;
}

 if (digitalRead(start_in) == 1 && left_start_try == 0 && first_sig_detect_time >= retention) 
  {
   left_start_try = 3;   // указываем что нужно попытаться трижды запустить движок
  } 
}

[/code]

 

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Да в общем нормально, но есть ли понимание того, что все запустится ТОЛЬКО если в течении 2000 мс держать сигнал поднятым? 2000 не дохрена ли? Просто спросил.

xellow
Offline
Зарегистрирован: 12.03.2016

Я думаю 1с будет достаточно, просто в цыфры особо не вдавался, пока занимался формой). А можно вынести 

static const unsigned int retention = 2000; // длительность отслеживания нажатия и удержания.
07 static int first_sig_detect_time = 0; // длительность отслеживания нажатия и удержания.

в начало скетча, где перечисляются все принятые переменные?

Клапауций 232
Offline
Зарегистрирован: 05.04.2016
слушай, дело, конечно, хозяйское, но какой смысл разбирать велосипед на детали и спрашивать, как тебе ехать на одной педали?
 
т.е. класс самодостаточен для реализации на его основе всех твоих хотелок без редактирования кода класса.
wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Читайте Евгения.П, а не его оппонентов ;) и НЕ ИСПОЛЬЗУЙТЕ глобальные переменные без четко понимаемой необходимости.

Если эта переменная нужна только в этой функции, то пусть она только там и будет. Если ее значение нужно сохранять от вызова-к-вызову, то она должна быть "static". Это вопрос изначальной культуры програмирования.  При маленьких скетчах на один файл - значения не имеет, но как привычка - можно выработать хорошую, а можно дурную.

Привыкайте мыслить объектно, а не всей кучей программы в целом. Чек-старт - это отдельный объект, в нем варится некая кухня, которая другим объектам АБСОЛЮТНО не нужна, так и пусть будет им не видна. Даже когда Вы пишите на "чистом", процедурном Си, мыслить объектно - все равно правильнее, не обязательно оформлять в стиле С++.

Если алгоритм сложнее включения лампочек на лестнице, то не всегда легко представить его работу целиком, всего. Поэтому мы делим его на независимые объекты. В Вашем случае - отдельный объект "брелок" он может передавать (возможно и принимать) сообщения и иметь внутреннее состояние. Есть объект "двигатель", объект "система запуска". У каждого есть свои состояния и возможность передавать и принимать сообщения. Часы, в смысле реальное время - это тоже объект, и от него Вы получаете сообщения функцией millis().

.....

Короче "Остапа понесло", простите. Холиваров на форуме начитался и не выдержал.

--------

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

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Оппоненты ЕвгенияП нигде не рекомендуют совать всё подряд в общие глобалы и разбирать велосипеды. Тем не менее, сами по себе, глобалы не являются ни добром ни злом. Собственно безаппеляционность этого предрассудка "глобалы - зло" и есть "обучение плохому" .. все зависит исключительно от применения: "по делу или по предрассудку".

В данном конкретном случае, Клапауций - безусловно прав. Нет никакой надобности разбирать велосипед. Потом неожиданно, может выйти боком. Желание автора вынести в "общую кучу настроек решается проще: сделайте в своей общей куче #define MAX_RETENTION 2000 или сколько вам хочется и воткните его в оператор присваивания. Делов-то.

Jatixo
Offline
Зарегистрирован: 13.01.2016

Возможно при включении устройства сигнал меньшей длины, например 50 мс, а когда нормальный сигнал, то допустим 500 мс, тогда можно на этом свойстве и узнавать. В общем делайте миниосциллограф на ардуино =)