Управление закрытием дверей автомобиля. Ардуино-Нано.

Lomonosov
Lomonosov аватар
Offline
Зарегистрирован: 12.01.2017

Всех приветствую!

Осмелюсь создать отдельную тему по успешному завершению своего мини-проекта.

Изначальна задача ставилась использовать подаренную Андурину-НАНО с минимальным вложением средств в качестве автоматического запирания (блокировки) дверей автомобиля после каждого открытия. Чтобы сразу избежать ненужного флуда, скажу, что я не параноик, не любитель тонировок, машина отечественная (Шевроле-Нива), и мне надо это не для самосохранения, а для сохранения моих денег и документом, т.к. среди коллег были случаи именного такой схемы воровства – один отвлекает глупым вопросом, второй крадет барсетку.

 

Для реализации этой задачи был задействован такой простой алгоритм:

Двери должны блокироваться по достижению определенной скорости (10км/ч). Двери не должны самопроизвольно блокироваться (если машина случайно покатится). Задачи разблокировки не ставилась, т.к. сигналка отлично умеет разблокировать двери по сигналу выключения зажигания.

Чтобы не завязываться с режимом энергосбережения и возможной перезагрузки контроллера Ардуину запитал от включенного зажигания (не постоянное питание). Необходимые входы:  опрос датчика скорости (ДС) на предмет достижения нужной скорости, опрос датчика педали газа (ДППА) на предмет наличия водителя за рулем, опрос концевика дверей на предмет открытости/закрытости дверей. Выходной сигнал – отрицательный слаботочный импульс на канал запирания блока управления дверьми.

Алгоритм программы тоже прост.

Если двери открыты, то они не заперты (разблокированы). Если двери закрыты, разблокированы, скорость больше 10км/ч и педаль газа нажата, то двери нужно заблокировать.

 

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

http://arduino.ru/forum/apparatnye-voprosy/podklyuchenie-nano-k-bortovoi-seti-avtomobilya

http://arduino.ru/forum/programmirovanie/zapis-i-chtenie-eeprom?page=1#comment-275854

http://arduino.ru/forum/apparatnye-voprosy/naskolko-kretichno-veshat-nagruzku-na-emmiter-u-n-p-n-tranzistora

http://arduino.ru/forum/programmirovanie/anti-drebezg#comment-252078

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

Но в процессе чтения форума, изучения схем автомобильных контроллеров и бортовых компьютеров пришло более ясное понимание аппаратной части. Сначала я долго не мог понять почему в автомобильных контроллерах подключение к портам  контроллеров ведется напрямую только через резисторы 22кОм, без всяких делителей напряжения, защитных стабилитронов и пр.:

Схема Январь 7.2 (ПДФ-файл 1,4 мБ)

Потом наткнулся на устройство порта МК (а именно на защитные диоды ) и все стало на свои места. Действительно, имея защитные диоды уже можно не бояться скачков и всплесков напряжения, просадок и т.п. – на входе порта будет устойчивый стабильный сигнал. Сначала была попытка прочитать 400 с лишним страниц даташита на АТмегу с  базовыми знаниями английского. Эта попытка к большому успеху не привела. Какой ток выдерживают эти диоды я так и не нашел. Поэтому пришлось поверить отдельным сообщениям в форуме, что это порядка 1мА. Если учесть, что без последствий нужно пережить скачки напряжения под 100в. То сопротивление резистора должно быть 100/0,001=100кОм. Но полностью от делителя напряжения отказываться не стал, т.к. при обрыве провода порт повисал в воздухе, а также уже плата была вытравлена под делители. Оставил половинный делитель 100 на 100 кОм. Из того же даташита устойчивая единица это 2,65В при питании 5,0В, т.е. на входе делителя достаточно 5,3 вольта. С другой стороны, при штатном напряжении 14В на входе ток через защитный диод (14-5) /100000 - 5/100000=40 мкА.

Схемы считывания показаний с ДС и концевиков дверей получились такими:

 

Схему считывания напряжения с датчика педали газа так же сделал на делителе 1:2 чтобы при обрыве порт не весел в воздухе с одной стороны (100кОм), и чтобы не спалить Ардуину при неправильной конфигурации порта на выход или ошибочном подключении датчика с другой стороны (47кОм):

Практика показала, что делитель не вносит коррективы в показания снимаемые автомобильным контроллером с датчика. Ошибки не появляются. Ну и прямые расчеты в лоб показывают, что делитель «подтягивает» датчик педали к земле меньше чем на 0,1% во всем диапазоне, что меньше погрешности изготовления самого механического потенциометра датчика.  

Выход (просадка на массу) управления каналом запирания блока ЦЗ организовал обычным транзисторным ключом:

Так же в процессе тестирования и изготовления пришла мысль управлять фарами дневных ходовых огней (ДХО). По закону ДХО должны включаться при включении зажигания, но это не всегда удобно. Что бы зимой с утра при дистанционном запуске не светить фарами по окнам нижних этажей было решено их включать по датчику скорости. 

Также потребовалось к андурине подцепить сервисную кнопку. Это было вызвано необходимостью иметь возможность выключать автозакрытие дверей (в деревне при частых коротких поездках например таскание хлыстов при заготовке дров или наливание воды в баню в зимний период). Так же этой кнопкой предусмотрел  возможность выключать ДХО в ручном режиме (при длительном стоянии в пробках или на ж/д переездах). Алгоритм кнопки  - длинное нажатие включает/выключает автозакрытие дверей, короткое нажатие управляет ДХО.

Выход на ДХО должен управлять по плюсу 12 вольт, поэтому пришлось делать каскадную схему на двух транзисторах:

Сервисная кнопка управляется по минусу. Внутреннюю подтяжку задействовать не стал, поставил внешнюю. Резистрор R11 защитный от неправильной конфигурации порта

Питание. Т.к выходной ток ардуины – это управление транзисторными ключами с током 1мА на каждую базу транзистора, то плата ардуино будет потреблять свои 20+/-2мА заявленные в даташите (и проверенные тестером в реальности). Поэтому делать внешний линейный стабилизатор на том же LM7805 я не стал. Только защитился от скачков напряжения:

Диод Vd1 защитный от переплюсовки. Резистор R1 токоограничивающий. Максимальное падение напряжения 2В, ну пусть даже будет 3В (при токе потребления 30мА). Супрессор Vd2 - защита от скачков. Конденсаторы  С1, С2, С3, С4 сглаживающие. Индуктивность L1 для блокирования скачков напряжения.

 

Теперь по коду. Основные алгоритмы достаточно просты и написаны в комментариях. Больше всего места занимают коды для антидребезга (на датчиках и на кнопке). Конечно можно воспользоваться специальными библиотеками, вставить код из «титанового велосипеда», пользоваться объектно-ориентированным программированием - было бы все сильно красивее и короче.  Но я хотел сам))) Поэтому получилось длинно и не красиво. Зато я точно знаю, что это работает как надо.

Короткое нажатие кнопки проверяется по предыдущей инверсии (если менее 0,5сек – значит короткое нажатие). Длинное нажатие проверяется по времени непрерывного удержания с последней инверсии кнопки (если прошло больше секунды однократно возвращается признак длительного нажатия).

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

Функции приводимое исполнительные механизмы написаны с использованием задержки времени. Сначала написал как многие советует с помощью засечек времени, чтобы LOOP не прерывался, но потом подумал, что в моем случае это даже больше вредно чем полезно. Зачем во время сильного скачка токопотребления бортсети (запирание или мырг) иметь возможность сделать еще один скачек токопотребления.

Код из-за длины спрятал под спойлер:

  #include <EEPROM.h>
  boolean DPPA = 0, dppa_tmp = 0;                         //Флаги педали газа: 0 - педаль не нажата, 1 - педаль нажата
  boolean DS = 0, ds_tmp = 0;                             //Флаги датчика скорости: 0 - скорость недостаточна, 1 - скорость в норме
  int DRL = 2; boolean drl_tmp = 0;                       //Флаги ДХО: 0 - выключено, 1 - включено, 2 - режим ожидания (авторежим)
  boolean ESW = 0, esw_tmp = 0;                           //Флаги состояние коцевика дверей: 0 - двери открыты, 1 - закрыты
  boolean LOCK = 0, lock_tmp =0;                          //Флаги запертых дверей: 0 - двери незаперты, 1 - заперты
  byte UPR;                                               //Флаг управлением дверей: 0 - не управлять, 1 - управлять (хранится в ЕЕПРОМ)
  boolean BOTTON = 0, botton_tmp = 0, bot_fl = 0;         //Положение и флаги кнопки: 0 - ненажата, 1 - нажата
  int Press = 0;                                          //Отклик кнопки: 0 - не нажата, 1 - короткое нажатие, 2 - длинное нажатие
  volatile unsigned long time_hz = 0;                     //Время предудущего прерывания датчика скорости
  volatile unsigned int HZ = 0;                           //Частота датчика скорости
  unsigned long time_esw = 0, time_dppa = 0, time_ds = 0, time_drl = 0, time_botton = 0, time_BOTTON = 0, time_upr = 0;//Засечки времени
                                      
  void setup() {                                          //Вход педали газа на А0
    pinMode(19, INPUT);                                   //Вход кнопки управления А5(Д19): 0 - нажата, 1 - отпущена (инвертированная)
    pinMode(16, INPUT);                                   //Вход концевика дверей А2(Д16): 0 - двери открыты, 1 - закрыты
    pinMode(10, OUTPUT);                                  //Выходной сигнал на закрытие Д10
    pinMode(17, OUTPUT);                                  //Выходной сигнал на ДХО А3(Д17)
    attachInterrupt(1, Speed, RISING);                    //Прерывание по датчику скорости, пин Д3
    UPR = EEPROM.read(0);                                 //Считать флаг управления дверей.
  }
  
/***Мониторит концевик и все датчики в реальном времени***/
  void loop() {
    if(micros()-time_hz > 1000000) HZ = 0;                //Если с датчика скорости больше секунды не было отклика, скорость нулевая
    Bounce_DS(20<HZ && HZ<300);                           //Проверка датчика скорости на установку флага для запирания (12км/ч-180км/ч, HZ = V*6/3.6)
    if(DRL==2) Bounce_DRL(10<HZ && HZ<300);               //Проверка датчика скорости в режиме ожидания для ДХО (6км/ч-180км/ч, HZ = V*6/3.6)   
    Bounce_ESW(digitalRead(16));                          //Считываем концевик и проверяем на дребезг     
    Bounce_Botton(!digitalRead(19));                      //Считываем кнопку (с инверсией) и проверяем на дребезг
    Bounce_DPPA(analogRead(0) > 140);                     //Проверка педали газа на установку флага (0,78V->1,03V 105+35)
  
    if(Press == 2) {                                      //Если было длинное нажатие,
      UPR = !UPR;                                         //переключить управление ЦЗ
      EEPROM.write(0, UPR);                               //и запомнить.
      if(DRL==1) {                                        //Если ДХО включены,
        digitalWrite(17, 0);                              //моргнуть выключением на 0,5 сек.
        delay(500);
        digitalWrite(17, 1);                           
        delay(500);
      }
      else {                                              //Если ДХО выключены,
        digitalWrite(17, 1);                              //моргнуть включением на 0,5 сек.
        delay(500);                                       
        digitalWrite(17, 0);                           
        delay(500);
      }
    }
 
    if(Press == 1){                                       //Если было короткое нажатие,
      if(DRL==1 && HZ==0){                                //если ДХО включены и скорость нулевая,
        DRL = 2;                                          //перевести ДХО в автоматический режим и
        digitalWrite(17, 0);                              //выключить ДХО
      }
      else{                                               
        if(DRL==2) DRL = 1;                               //включить ДХО если был режим ожидания,
        else DRL = !DRL;                                  //инвертировать ДХО в ручном режиме  
        digitalWrite(17, DRL);                            //и подать соответсвующий сигнал на выход ДХО
      }
    }
  
    if(!ESW) LOCK = 0;                                    //Если двери открыты, то они не заперты          
    if(ESW && !LOCK && DPPA && DS && UPR) Zakrdv();       //Если двери закрыты, не заперты, выставлены флаги датчиков и управления, то запереть двери
  }
  
/***Пытается запереть двери при благоприятных условиях***/
  void Zakrdv() {                                         //Процесс запирание 
    unsigned long time_zakr = millis();                   //Поставить засечку времени.
    while(millis()-time_zakr < 500){                      //Полсекундная задержка на полное защелкивание замков (если двери закрылись на ходу)
      Bounce_ESW(digitalRead(16));                        //Продолжаем мониторить концевик
      if(!ESW) return;                                    //Резет, если двери открылись внезапкно
    }
    digitalWrite(10, 1);                                 
    delay(200);                                           //Подать импульс 0,2 сек. на запирание
    digitalWrite(10, 0);                                
    LOCK = 1;                                             //Выставить флаг запертости.
    time_zakr = millis();                                 //Обновить засечку времени.     
    while(millis()-time_zakr < 1500){                     //Подождать запирание дверей 1,5 секунды (из-за ступенчатой работы активаторов) 
      Bounce_ESW(digitalRead(16));                        //Продолжаем мониторить концевик
      if(!ESW) LOCK = 0;                                  //Сбросить флаг, если двери открылись сразу за закрытием
    }
  }
  
/***Мониторит скорость в реальном времени***/
  void Speed() {                                
    HZ = 1000000/(micros() - time_hz);                    //Определение частоты датчика скорости
    time_hz = micros();                                   //Засечка времени
  }
  
/***Проверка дребезга двери двухстороняя***/
  void Bounce_ESW(boolean tmp) {                          //Проверка дребезга концевика в течении 1/4 сек.
    if (tmp != esw_tmp){                                  //Если прошла инвертация, то
      esw_tmp = tmp;                                      //запомнить текущее состояние (временный флаг),
      time_esw = millis();                                //поставить засечку времени.
    }
    else 
      if (millis()-time_esw > 250){                       //Если прошло больше четверти секунды без инвертаций, то
        ESW = esw_tmp = tmp;                              //записать значение флага концевика (стабильное и временное).
      }
  }
  
/***Кнопка***/
  void Bounce_Botton (boolean tmp) {                      //Кнопка возвращает однократно: 1 - короткое нажатие, 2 - длинное, 0 - не нажата
    Press = 0;                                            //По умолчанию не нажата          
    if (tmp != botton_tmp){                               //Если прошла инвертация, то
      botton_tmp = tmp;                                   //запомнить текущее состояние (временный флаг),
      time_botton = millis();                             //поставить засечку времени (временную инвертации).
    }
    else                                                  
      if (millis()-time_botton > 50){                     //Если прошло больше 50 мс без инвертаций, и
        if (BOTTON != tmp){                               //1. Если предыдущее положение кнопки отличается от текущего (т.е. было нажатие, а не дребезг), и
          if (millis()-time_BOTTON < 500 && BOTTON)       //предыдущая инвертация была меньше 0,5 сек. и инвертация была нажатием, то
            Press = 1;                                    //было короткое нажатие. 
          BOTTON = tmp;                                   //запомнить текущее положение кнопки
          time_BOTTON = millis();                         //обновить засечку времени стабильной инвертации
          bot_fl=1;                                       //поставить "флаг длительного нажатия"
        }
        if (millis()-time_BOTTON > 1000 && BOTTON && bot_fl){   //2. Если кнопка удерживается дольше 1 сек в положении нажатия и флаг разрешает длительное нажатие 
          Press = 2;                                      //было длинное нажатие
          bot_fl = 0;                                     //сбросить "флаг длительного нажатия", чтобы функция повторно не возвращала 2
        }
      }
  }
  
/***Проверка дребезга педали газа на стабильный положительный отклик***/
  void Bounce_DPPA (boolean tmp) {                        //Проверка педали газа в течении 1/4 сек.
    if(!tmp){                                             //При отрицательном отклике,                                       
      DPPA = dppa_tmp = 0;                                //обнулить флаги,
      return;                                             //и выйти.
    }
    if (tmp > dppa_tmp){                                  //Если прошел положительный фронт,
      dppa_tmp = 1;                                       //поднять временный флаг,
      time_dppa = millis();                               //поставить засечку времени.
    }
    else                                                  //Если идут непрерывные положительные отклики,
      if (millis()-time_dppa > 250) DPPA = 1;             //и, если прошло больше четверти секунды, поднять основной флаг.
  }
  
/***Проверка дребезга датчика скорости на стабильный положительный отклик***/   
  void Bounce_DS (boolean tmp) {                          //Проверка стабильности ДС в течении 1/2 сек.
    if(!tmp){                                             //При отрицательном отклике,
      DS = ds_tmp = 0;                                    //обнулить флаги,
      return;                                             //и выйти.
    }
    if (tmp > ds_tmp){                                    //Если прошел положительный фронт,
      ds_tmp = 1;                                         //поднять временный флаг,
      time_ds = millis();                                 //поставить засечку времени.
    }
    else                                                  //Если идут непрерывные положительные отклики,
      if (millis()-time_ds > 500) DS = 1;                 //и, если прошло больше пол секунды, поднять основной флаг.
  }
  
/***Проверка дребезга датчика скорости на стабильный положительный отклик***/   
  void Bounce_DRL (boolean tmp) {                         //Проверка стабильности ДС в течении 1/2 сек.
    if(!tmp){                                             //При отрицательном отклике,
      drl_tmp = 0;                                        //обнулить флаг,
      return;                                             //и выйти.
    }
    if (tmp > drl_tmp){                                   //Если прошел положительный фронт,
      drl_tmp = 1;                                        //поднять временный флаг,
      time_drl = millis();                                //поставить засечку времени.
    }
    else                                                  //Если идут непрерывные положительные отклики,
      if (millis()-time_drl > 500) {                      //и, если прошло больше пол секунды,
        DRL = 1;                                          //поднять основной флаг (больше к функции обращения не будет),
        digitalWrite(17, 1);                              //и зажечь ДХО.
      }
  }

Ну собственно фото аппаратной части и всего устройства целиком:

 

Собственно всё. Пока работает как задумано.

С уважением, Алексей!

satelit 2
Offline
Зарегистрирован: 04.12.2016

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

Lomonosov
Lomonosov аватар
Offline
Зарегистрирован: 12.01.2017

satelit 2, изучал этот вопрос. Ни одна сигналка не умеет это делать многократно в течении ездового цикла. И это очивидно по логике безопасности самопроизвольного закрытия. Сигналка это может сделать однократно, и то при определенной последовательности действий. Если зимой сесть (открыть/закрыть двери) в машину, завести для прогрева, выйти чистить снег (открыть/закрыть двери), а потом сесть и поехать, ни одна сигналка двери не закроет, т.к. банально она "не знает" есть ли водитель за рулем, или машина сама катится. А вот если зимой выйти, почистить снег, потом сесть в холодныю машину (закрыв двери), после закрытия завести машину и поехать, то пожалуйста - сигналка знает, что двери закрыты, а потом кто-то завел машину и двери больше не открывались. Т.е. в машине есть человек.

Barpav
Offline
Зарегистрирован: 29.10.2017

У меня установлена сигналка Pantera SLK-675RS, она прекрасно справляется с поставленной задачей. Главное при настройки сигналки откалибровать холостые обороты. Дальше заводишь машину с брелка (на прогрев) или просто ключем, не важно, начинаешь ехать, как только обороты превысили ~1500 двери запираются. Разблокируются двери или с брелка, или с мастер саленоида, или когда глушишь двигатель, причем если после открытия дверей снова начинаешь движение, то при повышении оборотов двери запираются повторно. И, кстати, довольно таки умная сигналка, она понимает когда машина на прогреве и двери, когда машина без движения и на прогреве, не запирает.