Контроллер ДХО arduino nano

ATEMY

✩✩✩✩✩✩✩
1 Дек 2020
24
0
Всем привет!
Я в программировании не сильно силен, кое-что понимаю, но в целом «дуб».

Передо мной стала задача сделать себе контроллер ДХО на ардуино.

Принцип такой…
В качестве дхо используются лампы противотуманок, управление по плюсу через реле(пин D3)
К пину А1 подключен через делитель провод + с авто, через него контролируется напряжение, чтобы понять заведен двигатель или нет.
К пину D7 так же через делитель подключен провод от зажигания.
К пину D5 аналогично зажиганию подключен провод от ближнего света (+).

Логика должна быть такая.
1) если зажигание включено и напряжение в бортсети выше 13.1В - включаем ДХО через 2 секунды.
2) если при любых условиях включается ближний, дхо тухнут. Если горели то тухнут, если только выполнилось 1 условие и одновременно включен ближний, то и не загораются.
3) после выключения ближнего, но при соблюдении 1 условия, ДХО снова включаются через 2 секунды.
4) при выключении зажигания, дхо тухнут.
5) если перед выключением зажигания, ближний был включен 60 секунд, то мы включаем дхо на 30 секунд.(вежливый свет в ночное время).

все это я закинул в чатжпт, он с горем пополам выдал мне скетч ниже…
Работает все, кроме 4 пункта. Он хаотично может сам включиться при выключении зажигания, даже если ближний не был включен. Либо ближний буквально пару секунд проработал и выключился, но все равно после выключения зажигания включается реле на 30 секунд. Иногда даже когда ближний в момент выключения зажигания был выключен, а включался пару секунд назад. Реле выключается и сразу же включается снова на 30 секунд.

И этот косяк я так и не смог побороть с нейросетью. Его исправления уже ведут к полному отказу. Скетч вообще толком не работает.

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

В общем нужна помощь в поправке кода чтобы он работал. Что не так здесь? Может кто-то так подскажет(с учетом того что я не сильно силен тут, мне чем подробнее тем лучше), может за символическую плату доведет до ума?
В самом коде более менее ориентируюсь.

Заранее спасибо!

Скетч:
// Определение пинов
const int pinIgnition = 7;      // Пин зажигания
const int pinLowBeam = 5;       // Пин ближнего света
const int pinRelayDRL = 3;      // Пин реле ДХО (ПТФ)
const int pinVoltage = A1;      // Пин для измерения напряжения бортсети

// Пороговые значения
const float voltageThreshold = 13.1;   // Пороговое напряжение для включения ДХО
const unsigned long delayDRLOn = 2000;   // Задержка на включение ДХО (2 секунд)
const unsigned long delayDRLOff = 0;  // Задержка на выключение ДХО (1 секунд)
const unsigned long delayAfterIgnitionOff = 30000; // ДХО включены на 30 секунд после выключения зажигания
const unsigned long lowBeamCheckTime = 60000; // Время проверки, был ли включен ближний свет за 60 секунд

// Переменные
bool drlState = false;              // Состояние ДХО (включено/выключено)
unsigned long drlOnTimer = 0;       // Таймер для задержки включения ДХО
unsigned long drlOffTimer = 0;      // Таймер для задержки выключения ДХО
unsigned long lowBeamLastOnTime = 0; // Последнее время включения ближнего света
bool ignitionPrevState = false;     // Предыдущее состояние зажигания

// Настройки фильтрации
const int filterSamples = 5; // Количество образцов для фильтрации
int ignitionReadings[filterSamples]; // Массив для хранения образцов зажигания
int lowBeamReadings[filterSamples]; // Массив для хранения образцов ближнего света

void setup() {
  pinMode(pinIgnition, INPUT);
  pinMode(pinLowBeam, INPUT);
  pinMode(pinRelayDRL, OUTPUT);
  digitalWrite(pinRelayDRL, LOW); // По умолчанию ДХО выключено
  Serial.begin(9600);

  // Инициализация массивов
  for (int i = 0; i < filterSamples; i++) {
    ignitionReadings[i] = 0;
    lowBeamReadings[i] = 0;
  }
}

void loop() {
  // Чтение и фильтрация сигналов
  bool ignitionOn = filterInput(pinIgnition, ignitionReadings);
  bool lowBeamOn = filterInput(pinLowBeam, lowBeamReadings);
  float voltage = readVoltage();
  unsigned long currentTime = millis();

  // Лог для отладки
  Serial.print("Ignition: "); Serial.print(ignitionOn);
  Serial.print(", LowBeam: "); Serial.print(lowBeamOn);
  Serial.print(", Voltage: "); Serial.println(voltage);

  // Обновление времени включения ближнего света
  if (lowBeamOn) lowBeamLastOnTime = currentTime;

  // Условия управления ДХО
  handleDRL(ignitionOn, lowBeamOn, voltage, currentTime);

  // Обновляем предыдущее состояние зажигания
  ignitionPrevState = ignitionOn;
}

// Функция фильтрации входного сигнала
bool filterInput(int pin, int* readings) {
  // Сдвигаем значения в массиве
  for (int i = filterSamples - 1; i > 0; i--) {
    readings[i] = readings[i - 1];
  }
  readings[0] = digitalRead(pin); // Читаем текущее значение

  // Рассчитываем среднее значение
  int sum = 0;
  for (int i = 0; i < filterSamples; i++) {
    sum += readings[i];
  }
  return sum / filterSamples > 0.5; // Возвращаем истинное значение, если среднее больше 0.5
}

// Функция управления ДХО
void handleDRL(bool ignitionOn, bool lowBeamOn, float voltage, unsigned long currentTime) {
  // Условие для включения ДХО
  if (ignitionOn && voltage > voltageThreshold && !lowBeamOn) {
    if (!drlState) {
      if (drlOnTimer == 0) drlOnTimer = currentTime; // Запуск таймера
      if (currentTime - drlOnTimer >= delayDRLOn) {
        drlState = true;
        digitalWrite(pinRelayDRL, HIGH); // Включаем ДХО
        Serial.println("DRL включено");
      }
    }
  } else {
    drlOnTimer = 0; // Сбрасываем таймер включения
  }

  // Условие для выключения ДХО
  if (drlState && (!ignitionOn || voltage <= voltageThreshold)) {
      if (drlOffTimer == 0) drlOffTimer = currentTime; // Запуск таймера
      if (currentTime - drlOffTimer >= 1000) { // Задержка 1 секунда для стабилизации
          drlState = false;
          digitalWrite(pinRelayDRL, LOW); // Выключаем ДХО
          Serial.println("DRL выключено");
      }
  } else {
      drlOffTimer = 0; // Сбрасываем таймер выключения
  }

  // Если включен ближний свет, немедленно выключаем ДХО
  if (drlState && lowBeamOn) {
    drlState = false;
    digitalWrite(pinRelayDRL, LOW); // Выключаем ДХО
    Serial.println("DRL выключено из-за ближнего света");
  }

  // Проверка состояния ближнего света после выключения зажигания
  if (!ignitionOn && ignitionPrevState) {
    if (lowBeamLastOnTime + lowBeamCheckTime > currentTime) {
      if (!drlState) { // Если ДХО выключено, включаем
        drlState = true;
        digitalWrite(pinRelayDRL, HIGH); // Включаем ДХО на 30 секунд
        Serial.println("DRL включено на 30 секунд после выключения зажигания");
        unsigned long endTime = currentTime + delayAfterIgnitionOff;
        while (millis() < endTime) {
          // Ждем 30 секунд, ничего не делаем
        }
        drlState = false; // После задержки выключаем
        digitalWrite(pinRelayDRL, LOW);
        Serial.println("DRL выключено после задержки");
      }
    } else {
      drlState = false; // Оставляем ДХО выключенными
      digitalWrite(pinRelayDRL, LOW);
      Serial.println("DRL осталось выключено после выключения зажигания");
    }
  }
}

// Функция для чтения напряжения бортсети через делитель
float readVoltage() {
  int sensorValue = analogRead(pinVoltage);
  return sensorValue * (5.0 / 1023.0) * ((1000.0 + 470.0) / 470.0);  // Учитываем делитель напряжения
}
Еще нюанс. Сейчас, если выключить эту функцию «вежливого света» , выставить 0 вместо 30 секунд, то происходит двойной щелчок реле
 
Изменено:

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
Довести до ума - это скорее всего написать новый код, человеком разбирающемся в специфике авто.
Я в авто не разбираюсь.
Если вы понимаете, что написано в вашем коде и умеете составлять условия, то попробуйте сами поправить.
Например.
> Либо ближний буквально пару секунд проработал и выключился, но все равно после выключения зажигания включается реле на 30 секунд.
Находите блок отвечающий за это
// Проверка состояния ближнего света после выключения зажигания
if (!ignitionOn && ignitionPrevState) {
if (lowBeamLastOnTime + lowBeamCheckTime > currentTime) {

Ищите поиском , где еще встречается lowBeamLastOnTime

unsigned long lowBeamLastOnTime = 0; // Последнее время включения ближнего света
if (lowBeamOn) lowBeamLastOnTime = currentTime;
if (lowBeamLastOnTime + lowBeamCheckTime > currentTime) {

И видите, что это не соответствует требованию "4) если перед выключением зажигания, ближний был включен 60 секунд,"
Нигде не измеряется СКОЛЬКО горел ближний свет, есть только время КОГДА он горел последний раз.
Если следовать вашему построению программы, то ВОЗМОЖНАЯ модификация

unsigned long lowBeamLastOnTime = 0; // Последнее время ближнего света в сост ВКЛ
unsigned long lowBeamLastOffTime = 0; // Последнее время ближнего света в сост ВЫКЛ

if (lowBeamOn)
lowBeamLastOnTime = currentTime;
else
lowBeamLastOffTime = currentTime;

if (lowBeamOn && (currentTime - lowBeamLastOffTime > lowBeamCheckTime )) { // Если сейчас свет горит и горит достаточно долго

Если это вам понятно, то такими шагами исправьте сами. Если нет, то нет.
И замечание. Если ардуино будет работать без перезапуска больше 49 дней, то использовать millis надо правильно.
Переполнение счетчика millis не мешает измерению интервалов при правильном написании
lowBeamLastOnTime + lowBeamCheckTime > currentTime - НО ТАК НЕПРАВИЛЬНО
 

ATEMY

✩✩✩✩✩✩✩
1 Дек 2020
24
0
@Bruzzer,

Ну тут специфики авто как таковой нет. По сути все я расписал в условиях. Других нюансов по сути нет.
Единственное, да, ардуино будет работать всегда, пока акб не отключишь. Вот за это я не подумал, не знал что есть разница.

Если можете помочь или написать, как связаться с вами?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@ATEMY,
Для меня специфика в возможных наводках. Например используемый вами алгоритм фильтрации мне кажется сомнительным, но нужен ли он вообще и если да то какой, мне это не известно и не интересно.

Насчет помочь. То, что я написал в предыдущем сообщении вам понятно? Вы попробовали? Это была бесплатная помощь, решающая вопрос по п 4 (но не проверял).
Но если вам это не понятно, то помощь мало реальна. Возможно кто то сделает вам бесплатно, код не сложный. Если у вас есть желание потратить одну т.р. за ДОРАБОТКУ пишите в личку. (доработка означает что используется ваш код, а не другой возможно более правильный или оптимальный). Хотя повторюсь, предыдущее сообщение по моему решает ваш вопрос. Только еще замените неправильное использование millis
unsigned long endTime = currentTime + delayAfterIgnitionOff;
while (millis() < endTime) {
// Ждем 30 секунд, ничего не делаем
}

на просто
delay(delayAfterIgnitionOff);
 

ATEMY

✩✩✩✩✩✩✩
1 Дек 2020
24
0
@Bruzzer,


Спасибо за помощь! Получилось, все работает, вроде как. Пока что поставлю в машину, буду проверять на деле. Если что, никогда не поздно подключиться и переписать код.

Единственное. Вы говорили, что больше 49 дней это работать не будет правильно. Или замена на delay это исправление этого косяка?

Я думал, может после цикла событий ребунуть плату? Вроде завелся, включилось дхо, заглушился и прописать ресет?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
476
137
@ATEMY,
Если писать правильно, то работать будет всегда. У Гайвера в уроках тоже есть про millis.
millis() - ПредыдущееЗначение Условие Диапазон
currentTime - lowBeamLastOffTime > lowBeamCheckTime

Я тут подумал, что возможно условие некорректно, если отключение зажигания одновременно отключает и ближний свет. Тогда получается, что значение lowBeamOn может быть зависимым от конкретной схемы.
if (lowBeamOn && (currentTime - lowBeamLastOffTime > lowBeamCheckTime )) { // Если сейчас свет горит и горит достаточно долго
Поэтому, если зажигание одновременно отключает и ближний свет, то надо использовать другое условие.
 

ATEMY

✩✩✩✩✩✩✩
1 Дек 2020
24
0
@Bruzzer, Ну как правило на машинах при выключении зажигания выключается и ближний автоматом.

По крайней мере скетч вроде работает, как нужно
 

bort707

★★★★★★✩
21 Сен 2020
3,066
914
как правило на машинах при выключении зажигания выключается и ближний автоматом.
Далеко не всегда. У меня например ближний гаснет только когда дверь открываешь. А если выключить зажигание и сидеть в машине - так и будет гореть.
 

ATEMY

✩✩✩✩✩✩✩
1 Дек 2020
24
0
@bort707,
Ну я ж не казал что в 100% случаев.
Просто в большинстве случаев так. У меня на мазде, если авторежим стоит, то с зажиганием и выключаются, если вручную включить ближний, то будет гореть, пока АКБ не сядет.
Но я этим режимом пользуюсь только днем. Почему и хочу автоматизировать ПТФ, как ДХО, чтобы ручку один раз в авто поставить и забыть за свет.